三行代码  ›  专栏  ›  技术社区  ›  Community wiki

如何在Django中的contrib.auth中使电子邮件字段在模型User中唯一

  •  68
  • Community wiki  · 技术社区  · 3 月前

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

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

    代码中实现这一点的最佳位置在哪里?

    我想避免使用这个号码 字段[4] 。对用户来说更好 字段['email'] 但是 字段 不是字典,只有列表。

    另一个想法可能是打开一个新的票证并上传一个包含新参数的补丁 settings.py :

    AUTH_USER_EMAIL_UNIQUE = True
    

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

    19 回复  |  直到 14 年前
        1
  •  46
  •   Arne Babenhauserheide Ry4an Brase    6 年前

    注意: 下面的代码是为Django的旧版本编写的(之前 Custom User Models 介绍)。它包含竞赛条件,并且 应仅与的事务隔离级别一起使用 SERIALIZABLE 以及请求范围的事务。

    您的代码不起作用,因为字段实例的属性是只读的。我担心这可能比你想象的要复杂一点。

    如果您只使用窗体创建用户实例,则可以定义一个自定义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_data 从内部 清洁的

    由于表单字段是按照声明的顺序进行验证的,我认为偶尔在 <字段>_清洁的 方法,只要有问题的字段出现在它所依赖的所有其他字段之后。我这样做是为了使任何验证错误都与字段本身相关,而不是与表单相关。

        2
  •  27
  •   eillarra    9 年前

    使用 unique_together 以“不同”的方式?到目前为止,它对我有效。

    class User(AbstractUser):
        ...
        class Meta(object):
            unique_together = ('email',)
    
        3
  •  15
  •   jdavidls    12 年前

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

    from django.contrib.auth.models import User
    User._meta.get_field('email')._unique = True
    
        4
  •  14
  •   laz    5 年前

    在设置模块中:

    # 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
    
        5
  •  7
  •   Alan Viars    12 年前

    这太神奇了,但我找到了一个最适合我的解决方案!

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

    用法示例 here

        6
  •  6
  •   zVictor    12 年前

    你的表格应该是这样的。

    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
    
        7
  •  5
  •   Nids Barthwal    5 年前

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

    @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")
    

    请注意,这也确保了非空白电子邮件。然而,这并不能进行适当的表单验证,只是引发了一个例外。

        8
  •  3
  •   Fernando Freitas Alves    8 年前

    Django有一个 Full Example 关于如何替换和使用自定义用户模型的文档,以便您可以添加字段并使用电子邮件作为用户名。

        9
  •  2
  •   Parag    14 年前

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

        10
  •  2
  •   jb.    11 年前

    我认为正确的答案可以确保唯一性检查放在数据库中(而不是django一侧)。因为由于时间和比赛条件的原因,尽管例如 pre_save 进行适当的检查。

    如果你真的非常需要这个,我想你可以尝试以下方法:

    1. 将用户模型复制到您自己的应用程序中,并将字段电子邮件更改为唯一。
    2. 在管理应用程序中注册此用户模型(使用来自 django.contrib.auth.admin )
    3. 创建自己的身份验证后端,使用您的模型而不是django模型。
        11
  •  2
  •   2 revs, 2 users 96%<br/>user476955&#13;    10 年前

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

    使用自定义 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])
        #....
    
        12
  •  2
  •   Arun    8 年前

    在任何models.py文件中添加以下函数。然后运行makemigrations并迁移。在Djang1.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()
    
        13
  •  2
  •   bonidjukic    6 年前

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

    因此,可以使用这样的方法:

    REGISTRATION_FORM = 'registration.forms.RegistrationFormUniqueEmail'

    这是有文件记录的 here

    以下是链接 changelog entry

        14
  •  1
  •   Tomasz Zielinski    12 年前

    Django不允许直接编辑User对象,但您可以添加pre_save信号并实现唯一的电子邮件。对于创建信号,u可以跟随 https://docs.djangoproject.com/en/2.0/ref/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
    
        15
  •  1
  •   nikhil patil    5 年前

    在某处添加以下内容:

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

    然后执行类似于以下内容的SQL:

    ALTER TABLE auth_user ADD UNIQUE (email);
    
        16
  •  0
  •   mlissner    13 年前

    当我创建新用户时,这里的第一个答案对我来说是有效的,但当我试图编辑用户时,它会失败,因为我将用户名从视图中排除。是否有一个简单的编辑可以使检查独立于用户名字段?

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

    (很抱歉,这是作为回答发布的,但我缺乏将其作为评论发布的可信度。我不确定我是否理解Stackoverflow在这方面的逻辑。)

        17
  •  0
  •   user2772346    8 年前

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

    在您的设置.py中,您需要指定以下设置 AUTH_USER_MODEL='myapp.MyUser'。

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

        18
  •  -2
  •   amirouche    14 年前

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

        19
  •  -2
  •   Azur    7 年前

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

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

    这样,如果你试图用数据库中已经存在的电子邮件地址注册,你会收到消息:具有此电子邮件地址的用户已经存在。