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

Django自定义用户模型注册表单失效及create_user提示异常求助

Django自定义注册表单无法创建用户问题排查

问题现象

  • 通过manage.py shell可正常创建用户,但点击注册表单提交按钮后页面仅刷新,无用户创建操作
  • VS Code中Account.objects.create_user代码行标红,提示Cannot access attribute "create_user" for class "BaseManager[Account]"

相关代码

forms.py

from django import forms
from .models import Account

class RegistrationForm(forms.ModelForm):
    password = forms.CharField(widget=forms.PasswordInput(attrs={
        'placeholder': 'Enter Password',
    }))
    confirm_password = forms.CharField(widget=forms.PasswordInput(attrs={
        'placeholder': 'Confirm Password',
    }))

    email = forms.EmailField(
        max_length=255, help_text="Required. Add a valid email address.")

    document = forms.CharField(widget=forms.TextInput(attrs={
        'placeholder': '(CPF/RG/PASSPORT)',
    }), max_length=15, help_text="Enter a valid CPF, RG, or Passport.")

    address = forms.CharField(widget=forms.TextInput(attrs={
        'placeholder': '1337 Example St, FL, US',
    }), max_length=255, help_text="Enter your address.")

    class Meta:
        model = Account
        fields = ['first_name', 'last_name',
                  'username', 'phone_number', 'email', 'address', 'document', 'password']

    def __init__(self, *args, **kwargs):
        super(RegistrationForm, self).__init__(*args, **kwargs)
        self.fields['phone_number'].widget.attrs['placeholder'] = '(xx) x xxxx-xxxx'
        for field in self.fields:
            self.fields[field].widget.attrs['class'] = 'form-control'

models.py

from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager

class MyAccountManager(BaseUserManager):
    def create_user(self, first_name, last_name, document, phone_number, email, username, address=None, password=None, TCM_wallet=0):
        if not email:
            raise ValueError('User must have an email address.')
        if not username:
            raise ValueError('User must have a username.')
        if not document:
            raise ValueError('User must provide the document.')
        if not address:
            raise ValueError('User must provide the address.')

        user = self.model(
            email=self.normalize_email(email),
            username=username,
            first_name=first_name,
            last_name=last_name,
            phone_number=phone_number,
            document=document,
            address=address,
            TCM_wallet=TCM_wallet,
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, first_name, last_name, username, phone_number, document, email, password, address, TCM_wallet=0):
        user = self.create_user(
            email=self.normalize_email(email),
            username=username,
            first_name=first_name,
            last_name=last_name,
            document=document,
            phone_number=phone_number,
            address=address,
            password=password,
            TCM_wallet=TCM_wallet,
        )
        user.is_admin = True
        user.is_active = True
        user.is_staff = True
        user.is_superadmin = True
        user.save(using=self._db)
        return user

class Account(AbstractBaseUser):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    username = models.CharField(max_length=50, unique=True)
    email = models.EmailField(max_length=100, unique=True) 
    phone_number = models.CharField(max_length=50, blank=True) 
    document = models.CharField(
        max_length=15, unique=True, help_text='Required. Enter a valid document (CPF, RG, or Passport).') 
    address = models.CharField(max_length=100, blank=True, null=True) 
    TCM_wallet = models.FloatField(default=0, editable=True)

    # required fields
    date_joined = models.DateTimeField(auto_now_add=True)
    last_login = models.DateTimeField(auto_now=True)
    is_admin = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    is_superadmin = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username', 'first_name',
                       'last_name', 'document', 'address', 'phone_number',]

    objects = MyAccountManager()

    def __str__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        return self.is_admin

    def has_module_perms(self, app_label):
        return True

views.py

from django.shortcuts import render, redirect
from .forms import RegistrationForm
from .models import Account

def register(request): 
    if request.method == 'POST':
        form = RegistrationForm(request.POST)    
        if form.is_valid():
            first_name = form.cleaned_data['first_name']
            last_name = form.cleaned_data['last_name']
            email = form.cleaned_data['email']
            phone_number = form.cleaned_data['phone_number']
            document = form.cleaned_data['document']
            address = form.cleaned_data['address']
            password = form.cleaned_data['password']
            username = email.split('@')[0]

            user = Account.objects.create_user(
                first_name=first_name,
                last_name=last_name,
                email=email,
                document=document,
                address=address,
                password=password,
                username=username,
            )

            user.phone_number = phone_number
            user.save()
    else:
        form = RegistrationForm()
    context = {
        'form': form,
    }
    return render(request, 'accounts/register.html', context)

def login(request):
    return render(request, 'accounts/login.html')

def logout(request):
    # assuming 'login' is the name of your login url pattern
    return redirect('login')

问题排查与修复

1. 表单验证失败导致无操作

表单中添加了confirm_password字段,但未添加密码匹配验证逻辑,提交时会因密码不匹配直接验证失败,后续代码无法执行。需在RegistrationForm中添加密码验证的clean方法:

def clean(self):
    cleaned_data = super(RegistrationForm, self).clean()
    password = cleaned_data.get('password')
    confirm_password = cleaned_data.get('confirm_password')

    if password != confirm_password:
        raise forms.ValidationError("Passwords do not match!")
    return cleaned_data

同时,Meta.fields中无需包含username(view中通过email生成该值),需移除该字段避免渲染多余输入框:

class Meta:
    model = Account
    fields = ['first_name', 'last_name', 'phone_number', 'email', 'address', 'document', 'password']

2. create_user参数缺失

MyAccountManager.create_user方法要求传入phone_number参数,但view中调用时未传递,导致调用失败。需在view的create_user调用中补充该参数:

user = Account.objects.create_user(
    first_name=first_name,
    last_name=last_name,
    email=email,
    document=document,
    address=address,
    password=password,
    username=username,
    phone_number=phone_number
)

无需后续单独设置user.phone_number并二次保存。

3. VS Code类型提示错误

这是Django类型推断问题,可给MyAccountManager添加类型注解解决:

from typing import TypeVar

UserType = TypeVar('UserType', bound='Account')

class MyAccountManager(BaseUserManager[UserType]):
    def create_user(self, first_name: str, last_name: str, document: str, phone_number: str, email: str, username: str, address: str | None = None, password: str | None = None, TCM_wallet: float = 0) -> UserType:
        # 原方法内容不变

4. 表单提交后无反馈

用户注册成功后需添加重定向操作,避免停留在原页面,可在用户创建完成后添加:

return redirect('login')

内容的提问来源于stack exchange,提问作者Clovis Aquino Borges

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 17:05:56