【问题标题】:Make User email unique django使用户电子邮件唯一 django
【发布时间】:2019-04-26 22:26:39
【问题描述】:

当用户注册时,如何使 Django 用户电子邮件唯一?

forms.py

class SignUpForm(UserCreationForm):
    email = forms.EmailField(required=True)

    class Meta:
        model = User
        fields = ("username", "email", "password1", "password2")

    def save(self, commit=True):
        user = super(SignUpForm, self).save(commit=False)
        user.email = self.cleaned_data["email"]
        if commit:
            user.save()
        return user

我正在使用来自 django.contrib.auth.models 的用户。 我是否需要覆盖模型中的用户。目前该模型不引用用户。

views.py

class SignUp(generic.CreateView):
    form_class = SignUpForm
    success_url = reverse_lazy('login')
    template_name = 'signup.html'

【问题讨论】:

    标签: python django


    【解决方案1】:

    最好的答案是通过子类化AbstractUser 来使用CustomUser,并将唯一的电子邮件地址放在那里。例如:

    from django.contrib.auth.models import AbstractUser
    
    
    class CustomUser(AbstractUser):
        email = models.EmailField(unique=True)
    

    并使用AUTH_USER_MODEL="app.CustomUser" 更新设置。

    但是,如果您不需要将唯一的电子邮件存储在数据库中,或者可能不将其用作用户名字段,那么您可以更新表单的 clean 方法以进行验证。例如:

    from django.core.exceptions import ValidationError
    
    
    class YourForm(UserCreationForm):
    
        def clean(self):
           email = self.cleaned_data.get('email')
           if User.objects.filter(email=email).exists():
                raise ValidationError("Email exists")
           return self.cleaned_data
    

    更新

    如果您处于项目中期,那么您可以关注documentation 了解如何更改迁移,简而言之就是:

    1. 备份你的数据库
    2. 创建一个与 auth.User 相同的自定义用户模型,将其命名为 User(如此多对多的表保持相同的名称)并设置 db_table='auth_user'(因此它使用同一个表)
    3. 删除所有迁移文件(__init__.py 除外)
    4. 从表django_migrations中删除所有条目
    5. 使用python manage.py makemigrations 创建所有迁移文件
    6. python manage.py migrate --fake 运行虚假迁移
    7. 取消设置 db_table,对自定义模型进行其他更改,生成迁移,应用它们

    但是如果你刚刚开始,那么删除迁移目录中的 DB 和迁移文件,__init__.py 除外。然后创建一个新数据库,通过python manage.py makemigrations 创建新的迁移集并通过python manage.py migrate 应用迁移。

    对于其他模型中的引用,您可以将它们引用到settings.AUTH_USER_MODEL,以避免将来出现任何问题。例如:

    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
    

    它会自动引用当前的用户模型。

    【讨论】:

    • 所以当我尝试添加 CustomUser 时出现错误,因为还有其他模型使用 django.contrib.auth.User。试图弄清楚我需要做什么来扩展用户模型。我是否需要将所有引用从 User 更改为 CustomUser?
    • @mogoli 见我上面的评论。您需要删除 db、migrations 等,然后重新启动项目。
    • 我知道这是很久以前发布的,但我在上面帖子中的解决方案中遇到了 CustomUser 的问题。我有一个带有电子邮件字段的 CustomUser,我发现我得到了一个完整性当我想在管理面板中创建新用户时出错。错误消息是:“IntegrityError at /admin/users/customuser/add/。我在启动项目时创建了 CustomUser,但后来添加了电子邮件字段。我想知道这是否可能是问题。重复键值违反了唯一性约束 users_customuser_email_6445acef_uniq" 详细信息:键 (email)=() 已存在。 "。
    • @ruddra “但是如果您不需要将唯一的电子邮件存储在数据库中......”是什么意思,不是检查电子邮件在 UserForm 中是否唯一在User model 中存储唯一的电子邮件?例如如果我想使用电子邮件和密码作为登录的主要字段,那么将检查电子邮件在UserForm 中是否唯一完成我的工作,或者我应该通过从AbstractUser 继承来创建CustomUser 模型?
    【解决方案2】:

    这是一个工作代码

    在你的任何models.py中使用下面的代码sn-ps

    models.py

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

    django 版本:3.0.2

    参考:Django auth.user with unique email

    【讨论】:

    • 迄今为止将用户模型电子邮件地址限制为唯一的最简单方法 - 如果表中还没有非唯一值,您甚至可以将其合并到现有项目中。
    【解决方案3】:

    在 Django 的文档中有一个很好的例子 - https://docs.djangoproject.com/en/2.1/topics/auth/customizing/#a-full-example

    您必须将AbstractBaseUser 模型中的电子邮件字段声明为unique=True

    class MyUser(AbstractBaseUser):
        email = models.EmailField(
            verbose_name='email address',
            max_length=255,
            unique=True,
        )
        date_of_birth = models.DateField()
        is_active = models.BooleanField(default=True)
        is_admin = models.BooleanField(default=False)
    

    【讨论】:

    • 当我尝试这个时,没有任何反应,我的注册仍然可以接受重复的电子邮件
    • @mogoli 删除您的数据库和迁移。在settings.py中添加AUTH_USER_MODEL='new_user_model.MyUser'. Then run python manage.py makemigrations new_user. Lastly, run python manage.py migrate`。
    【解决方案4】:

    您可能对以下内容感兴趣:

    django-user-unique-email

    具有所需的唯一电子邮件字段和项目中期支持的可重用用户模型。

    如果存在,它定义了原始表(auth_user)的自定义用户模型重用。如果需要(当添加到现有项目时),它会以正确的顺序重新创建应用迁移的历史记录。

    如有任何反馈,我将不胜感激。

    【讨论】:

      【解决方案5】:

      Django 3.1 的工作代码

      models.py

      from django.contrib.auth.models import User
      
      User._meta.get_field('email')._unique = True
      SETTINGS.PY
      AUTHENTICATION_BACKENDS = [
           'django.contrib.auth.backends.ModelBackend'
      ]
      

      【讨论】:

        【解决方案6】:

        比使用AbstractBaseUser更好的方法

        #forms.py
        from django.core.exceptions import ValidationError
        from django.contrib.auth.models import User
        from django.contrib.auth.form import UserCreationForm
        from some_app.validators import validate_email
        
        def validate_email(value):
            if User.objects.filter(email = value).exists():
                raise ValidationError((f"{value} is taken."),params = {'value':value})
        
        class UserRegistrationForm(UserCreationForm):
                email = forms.EmailField(validators = [validate_email])
                
                class Meta:
                    model = User
                fields = ['username', 'email', 'password1', 'password2']    
            
        

        【讨论】:

        • 我认为这是最干净的方式。它会在表单的错误代码中显示错误。
        【解决方案7】:

        如果使用继承自AbstractBaseUserCustomUser 模型,您可以覆盖full_clean() 方法来验证您指定的模型字段unique=True 的唯一约束。这比表单(即FormClass)验证更安全。

        示例

        from django.contrib.auth.models import AbstractBaseUser
        from django.db import models
        
        class CustomUser(AbstractBaseUser):
            email = models.EmailField(unique=True)
            # ...
        
        
            def full_clean(self, **kwargs):
                """
                Call clean_fields(), clean(), and validate_unique() on the model.
                Raise a ValidationError for any errors that occur.
                """
                super().full_clean()
        

        注意:测试于Django 3.1

        【讨论】:

          【解决方案8】:

          简单的方法: 你可以给用户发信号 示例

          from django.db.models.signals import post_save, pre_save
          from django.dispatch import  receiver
          from django.contrib.auth.models import User
          from django.forms import ValidationError
          
          @receiver(pre_save, sender=User)
          def check_email(sender, instance, **kwargs):
              email = instance.email
              if sender.objects.filter(email=email).exclude(username=instance.username).exists():
                  raise ValidationError('Email Already Exists')
          

          【讨论】:

            【解决方案9】:

            通过表单验证改进解决方案

            与其引发ValidationError,不如使用add_error 方法,以便发送表单的所有错误,而不仅仅是ValidationError 引发的错误。

            class SignUpForm(UserCreationForm):
                email = forms.EmailField(max_length=254, help_text='Required. Inform a valid email address.')
            
                class Meta:
                    model = User
                    fields = ('username', 'email', 'password1', 'password2', )
            
                def clean(self):
                    cleaned_data = super().clean()
                    email = cleaned_data.get('email')
            
                    if User.objects.filter(email=email).exists():
                        msg = 'A user with that email already exists.'
                        self.add_error('email', msg)           
                
                    return self.cleaned_data
            

            【讨论】:

              猜你喜欢
              • 2011-01-17
              • 1970-01-01
              • 2011-08-12
              • 1970-01-01
              • 2015-04-15
              • 2016-09-02
              • 2013-01-06
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多