如何在模型用户中使用Django中的contrib.auth创建独特的电子邮件字段

时间:2009-07-21 15:42:24

标签: django django-models

我需要通过确保电子邮件字段条目唯一来修补contrib.auth的标准用户模型:

User._meta.fields[4].unique = True

代码中哪个地方最适合这样做?

我想避免使用数字字段[4] 。用户字段['email'] 更好,但字段不是字典,只有列表。

另一个想法可能是打开新故障单并在settings.py内上传带有新参数的补丁:

AUTH_USER_EMAIL_UNIQUE = True

有关在Django用户模型中实现电子邮件地址唯一性的最正确方法的任何建议吗?

19 个答案:

答案 0 :(得分:48)

  

<强>注意:   下面的代码是为旧版本的Django编写的(在引入Custom User Models之前)。它包含竞争条件,和   应仅用于SERIALIZABLE的事务隔离级别   和请求范围的事务。

您的代码无效,因为字段实例的属性是只读的。我担心这可能比你想的要复杂得多。

如果您只使用表单创建User实例,则可以定义强制执行此行为的自定义ModelForm:

from django import forms
from django.contrib.auth.models import User

class UserForm(forms.ModelForm):
    class Meta:
        model = User

    def clean_email(self):
        email = self.cleaned_data.get('email')
        username = self.cleaned_data.get('username')
        if email and User.objects.filter(email=email).exclude(username=username).exists():
            raise forms.ValidationError(u'Email addresses must be unique.')
        return email

然后只需在需要创建新用户的地方使用此表单。

顺便说一句,您可以使用Model._meta.get_field('field_name')按名称获取字段,而不是按位置。例如:

# The following lines are equivalent
User._meta.fields[4]
User._meta.get_field('email')

更新

Django文档建议您对涵盖多个表单字段的所有验证使用clean方法,因为它是在所有<FIELD>.clean<FIELD>_clean方法之后调用的。这意味着您(大部分)可以依赖于cleaned_dataclean中的字段值。

由于表单字段按照声明的顺序进行验证,我认为偶尔在<FIELD>_clean方法中放置多字段验证是可以的,只要有问题的字段出现在所有其他字段之后它就可以了依赖于取决于。我这样做是因为任何验证错误都与字段本身相关联,而不是与表单相关联。

答案 1 :(得分:32)

如何在&#34;不同的&#34;中使用unique_together呢?办法?到目前为止它对我有用。

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

答案 2 :(得分:15)

在设置模块中:

# Fix: username length is too small,email must be unique
from django.contrib.auth.models import User, models
User._meta.local_fields[1].__dict__['max_length'] = 75
User._meta.local_fields[4].__dict__['_unique'] = True

答案 3 :(得分:14)

这太棒了,但我为我找到了最好的解决方案!

django-registration 具有检查电子邮件字段唯一性的表单:RegistrationFormUniqueEmail

使用示例here

答案 4 :(得分:8)

您的表单应该是这样的。

def clean_email(self):
    email = self.cleaned_data.get('email')
    username = self.cleaned_data.get('username')
    print User.objects.filter(email=email).count()
    if email and User.objects.filter(email=email).count() > 0:
        raise forms.ValidationError(u'This email address is already registered.')
    return email

答案 5 :(得分:6)

只需在任何应用程序的models.py中使用以下代码

from django.contrib.auth.models import User
User._meta.get_field('email')._unique = True

答案 6 :(得分:6)

要确保用户(无论身在何处)使用唯一的电子邮件保存,请将其添加到您的模型中:

@receiver(pre_save, sender=User)
def User_pre_save(sender, **kwargs):
    email = kwargs['instance'].email
    username = kwargs['instance'].username

    if not email: raise ValidationError("email required")
    if sender.objects.filter(email=email).exclude(username=username).count(): raise ValidationError("email needs to be unique")

请注意,这也可以确保非空白电子邮件。但是,这不会进行表格验证,因为它会被占用,只会引发异常。

答案 7 :(得分:3)

Django在其文档中有Full Example如何替换和使用自定义用户模型,因此您可以添加字段并使用电子邮件作为用户名。

答案 8 :(得分:2)

此方法不会使电子邮件字段在数据库级别上唯一,但值得尝试。

使用自定义validator

from django.core.exceptions import ValidationError
from django.contrib.auth.models import User

def validate_email_unique(value):
    exists = User.objects.filter(email=value)
    if exists:
        raise ValidationError("Email address %s already exists, must be unique" % value)

然后在forms.py中:

from django.contrib.auth.models import User
from django.forms import ModelForm
from main.validators import validate_email_unique

class UserForm(ModelForm):
    #....
    email = forms.CharField(required=True, validators=[validate_email_unique])
    #....

答案 9 :(得分:2)

我认为正确的答案可以确保在数据库中放置唯一性检查(而不是在django端)。因为时间和竞争条件,您可能会在数据库中以重复的电子邮件结束,尽管例如pre_save进行了正确的检查。

如果你真的需要这么做,我猜你可能会尝试以下方法:

  1. 将用户模型复制到您自己的应用,并将字段电子邮件更改为唯一。
  2. 在管理员应用中注册此用户模型(使用django.contrib.auth.admin中的管理类)
  3. 创建自己的身份验证后端,使用您的模型而不是django。

答案 10 :(得分:2)

在任何models.py文件中添加以下函数。然后运行makemigrations并迁移。在Django1.7上测试

def set_email_as_unique():
    """
    Sets the email field as unique=True in auth.User Model
    """
    email_field = dict([(field.name, field) for field in MyUser._meta.fields])["email"]
    setattr(email_field, '_unique', True)

#this is called here so that attribute can be set at the application load time
set_email_as_unique()

答案 11 :(得分:2)

从1.2版(2015年5月11日)开始,有一种方法可以使用设置选项REGISTRATION_FORM动态导入任何选定的注册表单。

所以,人们可以使用这样的东西:

REGISTRATION_FORM = 'registration.forms.RegistrationFormUniqueEmail'

记录在案here

这是指向changelog entry的链接。

答案 12 :(得分:2)

Django不允许直接编辑User对象,但您可以添加pre_save信号并实现唯一的电子邮件。对于创建信号,您可以关注https://docs.djangoproject.com/en/2.0/ref/signals/。然后将以下内容添加到signals.py

 @receiver(pre_save, sender=User)
def check_email(sender,instance,**kwargs):
    try:
        usr = User.objects.get(email=instance.email)
        if usr.username == instance.username:
            pass
        else:
            raise Exception('EmailExists')
    except User.DoesNotExist:
        pass

答案 13 :(得分:2)

执行此操作的一种可能方法是在User对象上设置预保存挂钩,并拒绝保存表中已存在的电子邮件。

答案 14 :(得分:1)

在此处添加:

User._meta.get_field_by_name('email')[0]._unique = True     

然后执行类似于此的SQL:

ALTER TABLE auth_user ADD UNIQUE (email);

答案 15 :(得分:0)

这里的第一个答案是在我创建新用户时为我工作,但在我尝试编辑用户时失败,因为我从视图中排除了用户名。是否有一个简单的编辑,使检查独立于用户名字段?

我还尝试将用户名字段包含为隐藏字段(因为我不希望人们编辑它),但这也失败了因为django正在检查系统中的重复用户名。

(抱歉,这是作为答案发布的,但我缺乏将其作为评论发布的信誉。不确定我理解Stackoverflow的逻辑。)

答案 16 :(得分:0)

您可以使用自己的自定义用户模型来实现此目的。您可以使用电子邮件作为用户名或电话作为用户名,可以有多个属性。

在settings.py中,您需要指定以下设置 AUTH_USER_MODEL =&#39; myapp.MyUser&#39;。

这是可以帮助您的链接。 https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#auth-custom-user

答案 17 :(得分:-2)

来自用户继承的模型,正确地重新定义属性。它应该有效,因为它在django核心中没有用,因为它很简单。

答案 18 :(得分:-3)

我去了\Lib\site-packages\django\contrib\auth\models 在班级AbstractUser(AbstractBaseUser, PermissionsMixin): 我将电子邮件更改为:

email = models.EmailField(_('email address'), **unique=True**, blank=True)

如果您尝试使用数据库中已存在的电子邮件地址进行注册,您将收到消息:具有此电子邮件地址的用户已存在。