首页 文章

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

提问于
浏览
57

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

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

代码中最好的位置在哪里?

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

另一个想法可能是打开一张新票并在 settings.py 中上传一个带有新参数的补丁:

AUTH_USER_EMAIL_UNIQUE = True

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

19 回答

  • 6

    在这里添加:

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

    然后执行类似于这样的SQL:

    ALTER TABLE auth_user ADD UNIQUE (email);
    
  • 6

    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
    
  • 2

    注意:下面的代码是为旧版本的Django编写的(在引入自定义用户模型之前) . 它包含竞争条件,并且只应与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_datacleaned_data 中存在的字段值 .

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

  • 2

    以"different"方式使用unique_together怎么样?到目前为止它对我有用 .

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

    在设置模块中:

    # 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
    
  • 47

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

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

    用法示例here

  • 14

    你的表格看起来应该是这样的 .

    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
    
  • 0

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

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

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

  • 28

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

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

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

  • 2

    一种可能的方法是在User对象上使用预保存挂钩,并拒绝保存表中已存在的电子邮件 .

  • 0

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

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

    • 将用户模型复制到您自己的应用程序,并将字段电子邮件更改为唯一 .

    • 在管理员应用中注册此用户模型(使用 django.contrib.auth.admin 中的管理类)

    • 创建自己的身份验证后端,使用您的模型而不是django .

  • -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])
        #....
    
  • 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()
    
  • 1

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

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

    REGISTRATION_FORM = 'registration.forms.RegistrationFormUniqueEmail'

    记录here .

    这是changelog entry的链接 .

  • 1

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

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

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

  • 15

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

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

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

  • 2

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

  • 7

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

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

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

相关问题