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

如何用Django allauth实现分步注册页面?

如何将Django Form Tools表单向导与Allauth集成实现分步注册

前提

你已经基于Django Allauth实现了单页注册,现在需要拆分为两步注册流程,第二步表单内容可根据第一步输入动态调整,下面是具体实现方案:


1. 安装并配置Django Form Tools

首先安装依赖包:

pip install django-formtools

将formtools添加到项目的INSTALLED_APPS:

# settings.py
INSTALLED_APPS = [
    # ... 其他应用
    'formtools',
    'allauth',
    'allauth.account',
    # ...
]

2. 拆分注册表单为两步

根据需求将原有的UserCreationForm拆分为两个独立的表单,第二步表单可基于第一步数据动态调整字段:

第一步表单(Step1Form)

负责收集基础且可能影响第二步的字段(如用户名、邮箱):

# forms.py
from django import forms
from .models import User
from django.utils.translation import gettext_lazy as _

class Step1Form(forms.ModelForm):
    username = forms.CharField(label='User Name', widget=forms.TextInput(attrs={'placeholder': 'User Name'}))
    email = forms.EmailField(label='Professional email address', widget=forms.EmailInput(attrs={'placeholder': 'Your professional email'}))

    class Meta:
        model = User
        fields = ['username', 'email']

    def clean_username(self):
        username = self.cleaned_data["username"]
        if User._default_manager.filter(username=username).exists():
            raise forms.ValidationError(_("This username has already been taken."))
        return username

    def clean_email(self):
        email = self.cleaned_data["email"]
        if User._default_manager.filter(email=email).exists():
            raise forms.ValidationError(_("This email is already registered."))
        return email

第二步表单(Step2Form)

负责收集补充信息、密码和条款,支持基于第一步数据动态调整字段:

# forms.py
from django import forms
from .models import User
from django_countries.fields import CountryField
from django.utils.translation import gettext_lazy as _

class Step2Form(forms.ModelForm):
    password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput(attrs={'placeholder': 'Create password'}))
    password2 = forms.CharField(label=_("Password confirmation"), widget=forms.PasswordInput(attrs={'placeholder': 'Confirm password'}))
    terms = forms.BooleanField(verbose_name=_('I have read and agree to the terms and conditions'), required=True)

    class Meta:
        model = User
        fields = ['first_name', 'last_name', 'country', 'terms']
        widgets = {
            'first_name': forms.TextInput(attrs={'placeholder': 'First Name'}),
            'last_name': forms.TextInput(attrs={'placeholder': 'Last Name'}),
        }
        labels = {
            'country': _("Country of Practice"),
        }

    def __init__(self, *args, **kwargs):
        # 获取第一步的清理后数据
        self.step1_data = kwargs.pop('step1_data', None)
        super().__init__(*args, **kwargs)
        
        # 示例:根据第一步的邮箱域名动态添加字段
        if self.step1_data:
            email_domain = self.step1_data['email'].split('@')[-1]
            if email_domain == 'example.com':
                self.fields['company_id'] = forms.CharField(
                    label=_("Company ID"), 
                    required=True, 
                    widget=forms.TextInput(attrs={'placeholder': 'Your Company ID'})
                )

    def clean_password2(self):
        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 clean_terms(self):
        is_agreed = self.cleaned_data['terms']
        if not is_agreed:
            raise forms.ValidationError(_('You must agree to the terms and conditions'))
        return is_agreed

3. 创建Form Wizard视图

使用SessionWizardView处理两步表单流程,并集成Allauth的用户注册逻辑(如发送验证邮件):

# views.py
from formtools.wizard.views import SessionWizardView
from django.contrib.auth import login
from django.urls import reverse_lazy
from .forms import Step1Form, Step2Form
from .models import User
from allauth.account.utils import complete_signup, send_email_confirmation

class RegistrationWizard(SessionWizardView):
    template_name = 'registration/wizard.html'
    form_list = [('step1', Step1Form), ('step2', Step2Form)]
    success_url = reverse_lazy('users:detail')

    def get_form_kwargs(self, step=None):
        kwargs = super().get_form_kwargs(step)
        # 向第二步表单传递第一步的清理后数据
        if step == 'step2':
            kwargs['step1_data'] = self.get_cleaned_data_for_step('step1')
        return kwargs

    def done(self, form_list, **kwargs):
        # 合并两步表单的清理后数据
        cleaned_data = {}
        for form in form_list:
            cleaned_data.update(form.cleaned_data)
        
        # 创建用户
        user = User.objects.create_user(
            username=cleaned_data['username'],
            email=cleaned_data['email'],
            password=cleaned_data['password1'],
            first_name=cleaned_data.get('first_name', ''),
            last_name=cleaned_data.get('last_name', ''),
            country=cleaned_data['country'],
            terms=cleaned_data['terms']
        )

        # 集成Allauth的注册完成逻辑,处理邮箱验证等
        request = self.request
        complete_signup(
            request,
            user,
            email_verification=request.session.get('account_verified_email', 'optional'),
            redirect_url=self.success_url
        )

        # 自动登录用户(可选,根据需求调整)
        login(request, user)

        return super().done(form_list, **kwargs)

4. 替换Allauth默认注册URL

在项目的urls.py中,将Allauth的注册URL指向我们的向导视图:

# urls.py
from django.urls import path
from .views import RegistrationWizard
from allauth.account.views import LoginView, LogoutView

urlpatterns = [
    # 替换Allauth默认注册视图
    path('accounts/signup/', RegistrationWizard.as_view(), name='account_signup'),
    # 保留Allauth的其他核心视图
    path('accounts/login/', LoginView.as_view(), name='account_login'),
    path('accounts/logout/', LogoutView.as_view(), name='account_logout'),
    # ... 其他URL配置
]

5. 创建向导模板

创建模板registration/wizard.html,渲染当前步骤的表单并提供导航按钮:

{% extends "base.html" %}
{% load i18n %}

{% block content %}
<h2>{% trans "Sign Up" %} - Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</h2>

<form method="post">
    {% csrf_token %}
    {{ wizard.management_form }}
    
    {% if wizard.form.forms %}
        {{ wizard.form.management_form }}
        {% for form in wizard.form.forms %}
            {{ form.as_p }}
        {% endfor %}
    {% else %}
        {{ wizard.form.as_p }}
    {% endif %}

    <div class="form-actions">
        {% if wizard.steps.prev %}
            <button type="submit" name="wizard_goto_step" value="{{ wizard.steps.prev }}">{% trans "Back" %}</button>
        {% endif %}
        <button type="submit">
            {% if wizard.steps.current == wizard.steps.last %}
                {% trans "Sign Up" %}
            {% else %}
                {% trans "Next" %}
            {% endif %}
        </button>
    </div>
</form>
{% endblock %}

6. 关键注意事项

  • 确保Allauth的邮箱验证配置(如ACCOUNT_EMAIL_VERIFICATION)正确,complete_signup会根据配置发送验证邮件。
  • 动态调整第二步字段的逻辑可根据实际需求修改,比如基于第一步的其他字段(如用户名前缀、国家)调整表单。
  • 测试所有验证逻辑:用户名/邮箱重复、密码不匹配、条款未勾选等场景,确保与原单页表单的验证行为一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 01:15:31