You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django实现注册表单选身份后动态展示对应字段方案咨询

实现方案

1. 模板层(accounts/signup.html)动态字段显示逻辑

以下代码适配你现有表单的Bootstrap样式,通过原生JS监听身份选择项的变化,动态切换对应字段的显示状态:

{% extends 'base.html' %} <!-- 替换为你自己的基础模板 -->
{% block content %}
<div class="container mt-4">
  <h2>用户注册</h2>
  {% if error_message %}
  <div class="alert alert-danger">{{ error_message }}</div>
  {% endif %}
  <form method="post" novalidate>
    {% csrf_token %}
    <!-- 通用字段 所有身份都显示 -->
    <div class="mb-3">
      <label>{{ form.username.label }}</label>
      {{ form.username }}
      {% for error in form.username.errors %}
      <div class="text-danger">{{ error }}</div>
      {% endfor %}
    </div>
    <div class="mb-3">
      <label>{{ form.email.label }}</label>
      {{ form.email }}
      {% for error in form.email.errors %}
      <div class="text-danger">{{ error }}</div>
      {% endfor %}
    </div>
    <div class="mb-3">
      <label>{{ form.password.label }}</label>
      {{ form.password }}
      {% for error in form.password.errors %}
      <div class="text-danger">{{ error }}</div>
      {% endfor %}
    </div>
    <div class="mb-3">
      <label>{{ form.password_repeat.label }}</label>
      {{ form.password_repeat }}
      {% for error in form.password_repeat.errors %}
      <div class="text-danger">{{ error }}</div>
      {% endfor %}
    </div>
    <div class="mb-3">
      <label>{{ form.first_name.label }}</label>
      {{ form.first_name }}
      {% for error in form.first_name.errors %}
      <div class="text-danger">{{ error }}</div>
      {% endfor %}
    </div>
    <div class="mb-3">
      <label>{{ form.last_name.label }}</label>
      {{ form.last_name }}
      {% for error in form.last_name.errors %}
      <div class="text-danger">{{ error }}</div>
      {% endfor %}
    </div>
    <div class="mb-3">
      <label>{{ form.date_of_birth.label }}</label>
      {{ form.date_of_birth }}
      {% for error in form.date_of_birth.errors %}
      <div class="text-danger">{{ error }}</div>
      {% endfor %}
    </div>
    <div class="mb-3">
      <label>{{ form.country.label }}</label>
      {{ form.country }}
      {% for error in form.country.errors %}
      <div class="text-danger">{{ error }}</div>
      {% endfor %}
    </div>
    <div class="mb-3">
      <label>{{ form.gender.label }}</label>
      {{ form.gender }}
      {% for error in form.gender.errors %}
      <div class="text-danger">{{ error }}</div>
      {% endfor %}
    </div>
    <!-- 身份选择字段 -->
    <div class="mb-3">
      <label>{{ form.dentist_or_student.label }}</label>
      {{ form.dentist_or_student }}
      {% for error in form.dentist_or_student.errors %}
      <div class="text-danger">{{ error }}</div>
      {% endfor %}
    </div>
    <!-- 牙医专属字段 默认隐藏 -->
    <div class="mb-3" id="dentist-field" style="display: none;">
      <label>{{ form.specialty.label }}</label>
      {{ form.specialty }}
      {% for error in form.specialty.errors %}
      <div class="text-danger">{{ error }}</div>
      {% endfor %}
    </div>
    <!-- 学生专属字段 默认隐藏 -->
    <div id="student-fields" style="display: none;">
      <div class="mb-3">
        <label>{{ form.student_email.label }}</label>
        {{ form.student_email }}
        {% for error in form.student_email.errors %}
        <div class="text-danger">{{ error }}</div>
        {% endfor %}
      </div>
      <div class="mb-3">
        <label>{{ form.institution.label }}</label>
        {{ form.institution }}
        {% for error in form.institution.errors %}
        <div class="text-danger">{{ error }}</div>
        {% endfor %}
      </div>
    </div>
    <button type="submit" class="btn btn-primary">注册</button>
  </form>
</div>
<script>
// 监听身份选择变化
const identitySelect = document.querySelectorAll('input[name="dentist_or_student"]');
const dentistField = document.getElementById('dentist-field');
const studentFields = document.getElementById('student-fields');

identitySelect.forEach(radio => {
  radio.addEventListener('change', function() {
    if (this.value === 'dentist') {
      dentistField.style.display = 'block';
      studentFields.style.display = 'none';
    } else if (this.value === 'student') {
      dentistField.style.display = 'none';
      studentFields.style.display = 'block';
    } else {
      dentistField.style.display = 'none';
      studentFields.style.display = 'none';
    }
  })
})
</script>
{% endblock %}

2. 表单层(forms.py)校验逻辑优化

原代码所有字段默认必填,会导致选择对应身份时,未显示的字段为空触发校验错误,需修改为条件校验规则,同时修复原有拼写错误:

from django import forms
from django_countries.fields import CountryField
from django.contrib.auth.models import User

class RegistrationForm(forms.Form):
    Specialties = [
        ('pediatric','Pediatric'),
        ('oral surgeon','Oral Surgeon'),
        ('periodontist','Periodontist (Restorative; Esthetic)'),
        ('orthodontist','Orthodontist'), # 修复拼写错误
        ('endodontist','Endodontist'),
        ('prosthodontist','Prosthodontist'),
        ('oral pathologist','Oral Pathologist'),
        ('oral radiologist','Oral Radiologist'),
        ('public health dentist','Public Health Dentist'),
        ('research and academics','Research and Academics'),
    ]

    username = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}), required=True)
    email = forms.EmailField(widget=forms.EmailInput(attrs={'class':'form-control'}), required=True)
    student_email = forms.EmailField(widget=forms.EmailInput(attrs={'class':'form-control'}), required=False) # 先设为非必填,后续条件校验
    password = forms.CharField(widget=forms.PasswordInput(attrs={'class':'form-control'}), required=True)
    password_repeat = forms.CharField(widget=forms.PasswordInput(attrs={'class':'form-control'}), required=True)
    first_name = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}), required=True)
    last_name = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}), required=True)
    date_of_birth = forms.DateField(label = "Date of Birth", widget=forms.SelectDateWidget([x for x in range(1920,2024)]), required=True)
    country = CountryField().formfield(required=True)
    gender = forms.ChoiceField(widget=forms.RadioSelect, choices=[('male','Male'),('female','Female')], required=True)
    specialty = forms.CharField(widget=forms.Select(choices= Specialties), required=False) # 先设为非必填,后续条件校验
    institution = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}), required=False) # 先设为非必填,后续条件校验
    dentist_or_student = forms.ChoiceField(widget=forms.RadioSelect, choices=[('dentist','Dentist'),('student','Student')], required=True) # 修复拼写错误

    # 用户名重复校验
    def clean_username(self):
        username = self.cleaned_data['username']
        if User.objects.filter(username=username).exists():
            raise forms.ValidationError("用户名已存在")
        return username

    # 公共邮箱重复校验
    def clean_email(self):
        email = self.cleaned_data['email']
        if User.objects.filter(email=email).exists():
            raise forms.ValidationError("邮箱已被注册")
        return email

    # 条件校验
    def clean(self):
        cleaned_data = super().clean()
        identity = cleaned_data.get('dentist_or_student')
        # 牙医身份校验
        if identity == 'dentist':
            if not cleaned_data.get('specialty'):
                self.add_error('specialty', '请选择您的专科方向')
        # 学生身份校验
        elif identity == 'student':
            student_email = cleaned_data.get('student_email')
            if not student_email:
                self.add_error('student_email', '请填写学生教育邮箱')
            elif "@edu" not in student_email:
                self.add_error('student_email', '学生邮箱必须为@edu后缀')
            elif User.objects.filter(email=student_email).exists():
                self.add_error('student_email', '该学生邮箱已被注册')
            if not cleaned_data.get('institution'):
                self.add_error('institution', '请填写您的院校名称')
        # 密码一致性校验
        if cleaned_data.get('password') != cleaned_data.get('password_repeat'):
            self.add_error('password_repeat', '两次输入的密码不一致')
        return cleaned_data

3. 视图层(views.py)代码简化与bug修复

原代码存在大量重复逻辑,且有两处明显错误,修复后代码如下:

def user_register(request):
    template_name = 'accounts/signup.html'
    if request.method == 'POST':
        form = RegistrationForm(request.POST)
        if form.is_valid():
            data = form.cleaned_data
            # 公共字段赋值
            user = User.objects.create_user(
                username=data['username'],
                email=data['email'] if data['dentist_or_student'] == 'dentist' else data['student_email'],
                password=data['password']
            )
            user.first_name = data['first_name']
            user.last_name = data['last_name'] # 修复原来错误赋值为first_name的问题
            user.dentist_or_student = data['dentist_or_student'] # 修复原来取值错误的问题
            user.date_of_birth = data['date_of_birth']
            user.country = data['country']
            user.gender = data['gender']
            user.save()
            login(request, user)
            return HttpResponseRedirect('home')
    else:
        form = RegistrationForm()
    return render(request, template_name, {'form': form})

内容的提问来源于stack exchange,提问作者Leo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 15:45:03