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

django-authtools UserCreationForm 偶发KeyError 'email'问题求助

偶发KeyError: 'email'在Django后台添加用户表单(django-authtools)

问题描述

使用django-authtools的UserCreationForm自定义表单时,打开Django后台「添加用户」表单偶发KeyError: 'email',提示email不在self.fields[User.USERNAME_FIELD]中,且实际self.fields里确实没有email字段。

错误的偶发特征:

  • 主浏览器窗口报错时,无痕窗口可正常访问;
  • 重启应用后错误消失,但运行一段时间后会复现,目前只能靠重启解决。

相关代码

自定义UserCreationForm

class UserCreationForm(UserCreationForm):
    """
    A UserCreationForm with optional password inputs.
    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["password1"].required = False
        self.fields["password2"].required = False
        # If one field gets autocompleted but not the other, our 'neither
        # password or both password' validation will be triggered.
        self.fields["password1"].widget.attrs["autocomplete"] = "off"
        self.fields["password2"].widget.attrs["autocomplete"] = "off"

    def clean_password2(self):
        password1 = self.cleaned_data.get("password1")
        password2 = super().clean_password2()
        if bool(password1) ^ bool(password2):
            raise forms.ValidationError("Fill out both fields")
        return password2

add_fieldsets配置

add_fieldsets = (
        (
            None,
            {
                "description": (
                    "Enter the new user's name and email address and click Save."
                    " The user will be emailed a link allowing him/her to login to"
                    " the site and set his/her password."
                ),
                "fields": (
                    "first_name",
                    "last_name",
                    "email",
                    "is_pi",
                ),
            },
        ),
        (
            "Password",
            {
                "description": "Optionally, you may set the user's password here.",
                "fields": ("password1", "password2"),
                "classes": ("collapse", "collapse-closed"),
            },
        ),
    )

UserAdmin代码

@admin.register(User)
class UserAdmin(NamedUserAdmin):
    inlines = [CostUnitInline, OIDCGroupInline]
    form = UserChangeForm
    add_form = UserCreationForm

额外疑问:错误信息中common.admin.UserCreationForm将self引用为UserForm,这是否属于异常情况?

排查思路

  • 解决类命名冲突:自定义表单和父类同名(都叫UserCreationForm),可能导致初始化或导入时的混淆。尝试把自定义类改名(比如CustomUserCreationForm),同步更新UserAdmin的add_form引用,观察错误是否复现。
  • 检查表单字段缓存:Django生产环境会缓存表单类的字段定义,可能存在请求触发意外字段修改的情况。可以在自定义表单的__init__中强制确保email字段存在:
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if 'email' not in self.fields:
            self.fields['email'] = forms.EmailField(required=True)
        # 原有逻辑...
    
  • 验证用户模型配置:确认User模型的USERNAME_FIELD是否设置为'email'——django-authtools的NamedUserAdmin依赖这个配置处理用户名字段。若配置正确,可尝试在自定义表单中显式声明email字段。
  • 排查并发导致的类属性篡改:偶发问题可能和并发请求修改表单类属性有关。在__init__中添加日志,记录self.__class__.__name__和self.fields内容,错误出现时检查是否是类级别的fields被意外修改。
  • 溯源django-authtools源码:查看父类UserCreationForm的初始化逻辑,确认是否存在条件性移除email字段的逻辑,以及是否依赖add_fieldsets的配置格式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 01:40:09