Python-Django自定义用户模型下多类型用户注册表单与视图实现咨询
Hey there! Let's work through your multi-type user registration setup step by step. I’ll break this down into manageable pieces to get your tabbed registration forms up and running smoothly.
First: Fix Model Consistency
I noticed your PersonalBelow18 model uses multi-table inheritance (directly inheriting from User), while all other user type models use a OneToOneField to link to User. This inconsistency can lead to unexpected database behavior. Let’s align all models to use OneToOneField for uniformity:
# models.py class PersonalBelow18(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) dob = models.DateField(blank=False) customer_id = models.BigIntegerField(blank=False) collection_use_personal_data = models.BooleanField(blank=False)
Step 1: Build Registration Forms
We’ll create a base form for the core User fields, plus separate forms for each user type’s unique fields. This keeps things modular and easy to maintain.
# forms.py from django import forms from .models import User, PersonalBelow18, PersonalAbove18, Parent, GroupContactPerson # Base form for core User model fields class UserBaseForm(forms.ModelForm): password1 = forms.CharField(label='Password', widget=forms.PasswordInput) password2 = forms.CharField(label='Confirm Password', widget=forms.PasswordInput) class Meta: model = User fields = ('email', 'title', 'gender', 'contenst') def clean_password2(self): # Validate password match password1 = self.cleaned_data.get("password1") password2 = self.cleaned_data.get("password2") if password1 and password2 and password1 != password2: raise forms.ValidationError("Passwords don't match") return password2 def save(self, commit=True): # Save password in hashed format user = super().save(commit=False) user.set_password(self.cleaned_data["password1"]) if commit: user.save() return user # Forms for each user type's unique fields class PersonalBelow18Form(forms.ModelForm): class Meta: model = PersonalBelow18 fields = ('dob', 'customer_id', 'collection_use_personal_data') class PersonalAbove18Form(forms.ModelForm): class Meta: model = PersonalAbove18 fields = ('dob', 'customer_id', 'contact_email', 'contact_no', 'collection_use_personal_data') class ParentForm(forms.ModelForm): class Meta: model = Parent fields = ('contact_email', 'contact_no', 'collection_use_personal_data') class GroupContactPersonForm(forms.ModelForm): class Meta: model = GroupContactPerson fields = ('contact_email', 'contact_no', 'department', 'address')
Step 2: Handle Form Submissions in Views
Create a single view to manage all form submissions. It’ll validate the core user form first, then save the corresponding user type profile based on the selected tab.
# views.py from django.shortcuts import render, redirect from .forms import ( UserBaseForm, PersonalBelow18Form, PersonalAbove18Form, ParentForm, GroupContactPersonForm ) def register(request): if request.method == 'POST': user_form = UserBaseForm(request.POST) if user_form.is_valid(): # Save core user first user = user_form.save() # Get hidden user_type value from the submitted tab user_type = request.POST.get('user_type') # Save corresponding user type profile if user_type == 'PB': pb_form = PersonalBelow18Form(request.POST) if pb_form.is_valid(): profile = pb_form.save(commit=False) profile.user = user profile.save() elif user_type == 'PA': pa_form = PersonalAbove18Form(request.POST) if pa_form.is_valid(): profile = pa_form.save(commit=False) profile.user = user profile.save() elif user_type == 'Parent': parent_form = ParentForm(request.POST) if parent_form.is_valid(): profile = parent_form.save(commit=False) profile.user = user profile.save() elif user_type == 'GC': gc_form = GroupContactPersonForm(request.POST) if gc_form.is_valid(): profile = gc_form.save(commit=False) profile.user = user profile.save() # Redirect to login page after successful registration return redirect('login') else: # Initialize all forms for GET requests user_form = UserBaseForm() pb_form = PersonalBelow18Form() pa_form = PersonalAbove18Form() parent_form = ParentForm() gc_form = GroupContactPersonForm() context = { 'user_form': user_form, 'pb_form': pb_form, 'pa_form': pa_form, 'parent_form': parent_form, 'gc_form': gc_form } return render(request, 'registration/register.html', context)
Step 3: Build the Tabbed UI Template
Use Bootstrap (or custom CSS) to create tabbed sections. Each tab will show the core user fields plus the unique fields for that user type, with a hidden user_type field to tell the view which profile to save.
<!-- templates/registration/register.html --> <!DOCTYPE html> <html> <head> <title>Register</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css"> </head> <body> <div class="container mt-5"> <h2>Create Your Account</h2> <!-- Tab Navigation --> <ul class="nav nav-tabs mt-3" id="userTabs" role="tablist"> <li class="nav-item"> <a class="nav-link active" id="pb-tab" data-toggle="tab" href="#pb" role="tab">Personal (Under 18)</a> </li> <li class="nav-item"> <a class="nav-link" id="pa-tab" data-toggle="tab" href="#pa" role="tab">Personal (18+)</a> </li> <li class="nav-item"> <a class="nav-link" id="parent-tab" data-toggle="tab" href="#parent" role="tab">Parent/Guardian</a> </li> <li class="nav-item"> <a class="nav-link" id="gc-tab" data-toggle="tab" href="#gc" role="tab">Group Contact</a> </li> </ul> <!-- Tab Content & Form --> <div class="tab-content mt-3 p-3 border border-top-0" id="userTabContent"> <form method="post"> {% csrf_token %} <!-- Personal Under 18 Tab --> <div class="tab-pane fade show active" id="pb" role="tabpanel"> {{ user_form.as_p }} <input type="hidden" name="user_type" value="PB"> {{ pb_form.as_p }} </div> <!-- Personal 18+ Tab --> <div class="tab-pane fade" id="pa" role="tabpanel"> {{ user_form.as_p }} <input type="hidden" name="user_type" value="PA"> {{ pa_form.as_p }} </div> <!-- Parent Tab --> <div class="tab-pane fade" id="parent" role="tabpanel"> {{ user_form.as_p }} <input type="hidden" name="user_type" value="Parent"> {{ parent_form.as_p }} </div> <!-- Group Contact Tab --> <div class="tab-pane fade" id="gc" role="tabpanel"> {{ user_form.as_p }} <input type="hidden" name="user_type" value="GC"> {{ gc_form.as_p }} </div> <button type="submit" class="btn btn-primary mt-3">Register</button> </form> </div> </div> <!-- Bootstrap JS for tab functionality --> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js"></script> </body> </html>
Quick Notes for Improvement
- Custom Form Rendering: Replace
as_pwith Bootstrap form classes (likeform-control) for a cleaner look. - Error Handling: The view will automatically show form errors if submission fails—you can style these to match your UI.
- Field Prepopulation: Add JavaScript to reset fields when switching tabs, so users don’t see leftover data from other tabs.
内容的提问来源于stack exchange,提问作者Abdul Rehman

