在 Django 1.5 自定义用户模型中使用电子邮件作为用户名字段导致 FieldError

Posted

技术标签:

【中文标题】在 Django 1.5 自定义用户模型中使用电子邮件作为用户名字段导致 FieldError【英文标题】:Using email as username field in Django 1.5 custom User model results in FieldError 【发布时间】:2013-03-13 21:27:47 【问题描述】:

我想使用电子邮件字段作为自定义用户模型的用户名字段。 我有以下自定义用户模型继承 Django 的 AbstractUser 模型:

class CustomUser(AbstractUser):
    ....
    email = models.EmailField(max_length=255, unique=True)

    USERNAME_FIELD = 'email'

但是当我运行时

python manage.py sql myapp

我收到以下错误:

FieldError:类“CustomUser”中的本地字段“email”与基类“AbstractUser”中名称相似的字段发生冲突

我首先包含我自己的电子邮件字段的原因是添加unique=True 选项。否则我会得到:

myapp.customuser:USERNAME_FIELD 必须是唯一的。将 unique=True 添加到字段参数中。

现在,关于这个: https://docs.djangoproject.com/en/1.5/topics/db/models/#field-name-hiding-is-not-permitted 我如何可以做到这一点? (然后将字段命名为“user_email”或类似的名称)

【问题讨论】:

【参考方案1】:

您可以编辑您的CustomUser 以将email 字段属性更改为unique=True

将此添加到自定义用户类的末尾,如下所示:

class CustomUser(AbstractUser):
    ...
    USERNAME_FIELD = 'email'
    ...
CustomUser._meta.get_field_by_name('email')[0]._unique=True

请注意,我们正在更改 _unique 而不是 unique,因为后者是一个简单的 @property

这是一个 hack,我很想听听任何“官方”答案来解决这个问题。

【讨论】:

我不得不使用CustomUser._meta.get_field('email')._unique = True【参考方案2】:

伊恩,非常感谢你的聪明回应:)

但是,我已经为我“修补”了一个解决方案。

因为AbstractUser 也有一个username 字段,这对我来说完全没有必要 我决定创建我的“自己的”AbstractUser

通过继承 AbstractBaseUserPermissionsMixin,我保留了大部分 User 模型的内置方法,而无需添加任何代码。

我还利用这个机会创建了一个自定义 Manager 以消除在 username 字段中的使用:

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

class CustomUser(AbstractBaseUser, PermissionsMixin):
     ....
     email = models.EmailField(max_length=255, unique=True)
     first_name = ...
     last_name = ...
     is_active = ...
     is_staff = ...
     ....

     objects = CustomUserManager()

     USERNAME_FIELD = 'email'


class CustomUserManager(BaseUserManager):
     def create_user(self, email, password=None, **extra_fields):
          .....

     def create_superuser(self, email, password, **extra_fields):
          .....

这个解决方案确实会导致重复一些 Django 的内置代码(主要是 AbstractUser 中已经存在的模型字段,例如 'first_name'、'last_name' 等),但也会导致更清晰的用户对象和数据库表.

遗憾的是,1.5 中引入的USERNAME_FIELD 的灵活性不能用于实际上在所有现有约束下创建灵活的用户模型。

编辑:官方文档中有一个全面的工作示例:https://docs.djangoproject.com/en/dev/topics/auth/customizing/#a-full-example

【讨论】:

你的 create_superuser 方法包​​含什么? 它使用create_user:u = self.create_user(email, password, **extra_fields)创建一个没有username字段的用户对象(用email代替),设置超级用户“特殊”字段(is_staff, is_superuser, ...),然后保存该用户. CustomUserMananger 的实现,是必须的,或者我可以跳过它,默认django的工作人员将被应用? @andi 从我当时看到的情况来看,username 参数对于 Django 的 UserManager 下的 create_superuser 是必需的。因为我从我的用户模型中删除了 username 字段,所以当我尝试使用 Django 的内置“项目创建”过程(它使用 Django 的 createsuperuser 脚本,这反过来又通过 Linux shell 创建超级用户时,这给了我一个错误)使用来自CustomUser 模型管理器的create_superuser 方法)。在没有username 的情况下覆盖和定义create_superuser 解决了这个问题。 由于无法在之前的评论中插入链接(评论“太长”),这里是 Django 自己的UserManager【参考方案3】:

如果您的真正目标是唯一的“电子邮件”值,而忽略“用户名”值,那么您可以:

填写“用户名”,例如sha256(user.email).hexdigest()[:30]

以这种方式添加唯一性:

class User(AbstractUser):
    class Meta:
        unique_together = ('email', )

这会导致:

CREATE TABLE "myapp_user" (
    ...
    "email" varchar(75) NOT NULL,
    UNIQUE ("email")
)

按预期工作,非常简单。

【讨论】:

这是我在处理此问题时看到的最佳选择。非常感谢! 填写用户名的代码放在哪里? @AlwaysLearning 有很多方法,例如docs.djangoproject.com/en/dev/topics/auth/default/… @DenisRyzhkov 当我在我的代码中创建用户时这很好,但它是后端,例如allauth 创建用户。所以,我需要以某种方式对其进行检测...... @AlwaysLearning 尝试django-allauth.readthedocs.io/en/latest/… 或docs.djangoproject.com/en/4.0/topics/db/models/…【参考方案4】:

使用官方网站的示例:

https://docs.djangoproject.com/en/1.7/topics/auth/customizing/#a-full-example

这是一个符合管理员要求的自定义用户应用示例。此用户模型使用电子邮件地址作为用户名,并具有所需的出生日期;除了用户帐户上的简单管理员标志外,它不提供权限检查。该模型将与所有内置的身份验证表单和视图兼容,除了用户创建表单。此示例说明了大多数组件如何协同工作,但并非旨在直接复制到项目中以供生产使用。

【讨论】:

这实际上是要走的路 这一切真的只是为了改变 USERNAME_FIELD 吗?

以上是关于在 Django 1.5 自定义用户模型中使用电子邮件作为用户名字段导致 FieldError的主要内容,如果未能解决你的问题,请参考以下文章

无法在 Django 1.5 中使用自定义用户模型创建超级用户

何时在 Django 1.5 中使用自定义用户模型

我可以在不创建自定义用户的情况下更改 Django 1.5 中的 USERNAME_FIELD 吗?

如何在 django 1.5 中自定义用户模型

为 django 1.5 自定义用户模型子类化 django-registration 1.0 表单

将现有 auth.User 数据迁移到新的 Django 1.5 自定义用户模型?