【问题标题】:Using email as username field in Django 1.5 custom User model results in FieldError在 Django 1.5 自定义用户模型中使用电子邮件作为用户名字段导致 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”或类似的名称)

【问题讨论】:

    标签: django django-models django-1.5


    【解决方案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")
    )
    

    按预期工作,非常简单。

    【讨论】:

    【解决方案4】:

    使用官方网站的示例:

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

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

    【讨论】:

    • 这实际上是要走的路
    • 这一切真的只是为了改变 USERNAME_FIELD 吗?
    猜你喜欢
    • 1970-01-01
    • 2019-08-02
    • 2020-01-10
    • 2013-05-14
    • 2022-10-06
    • 2012-02-18
    • 2013-07-02
    • 1970-01-01
    • 2015-04-27
    相关资源
    最近更新 更多