在 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
。
通过继承 AbstractBaseUser
和 PermissionsMixin
,我保留了大部分 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 中的 USERNAME_FIELD 吗?