8

我创建了一个自定义 UserModel 并使用电子邮件作为主要身份验证 ID 而不是用户名。

它区分大小写的问题,因为它将 test@gmail.com,Test@gmail.com 视为 2 个不同的帐户。

我需要强制它同时处理 1 个帐户,忽略它是大写还是小写。

这是我的文件:

模型.py

class UserModelManager(BaseUserManager):
    def create_user(self, email, password, pseudo):
        user = self.model()
        user.name = name
        user.email = self.normalize_email(email=email)
        user.set_password(password)
        user.save()

        return user

    def create_superuser(self, email, password):
        '''
        Used for: python manage.py createsuperuser
        '''
        user = self.model()
        user.name = 'admin-yeah'
        user.email = self.normalize_email(email=email)
        user.set_password(password)

        user.is_staff = True
        user.is_superuser = True
        user.save()

        return user


class UserModel(AbstractBaseUser, PermissionsMixin):
    ## Personnal fields.
    email = models.EmailField(max_length=254, unique=True)
    name = models.CharField(max_length=16)
    ## [...]

    ## Django manage fields.
    date_joined = models.DateTimeField(auto_now_add=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELD = ['email', 'name']

    objects = UserModelManager()

    def __str__(self):
        return self.email

    def get_short_name(self):
        return self.name[:2].upper()

    def get_full_name(self):
        return self.name

views.py中的注册视图

def signup(request):
    if request.method == 'POST':
        signup_form = SignUpForm(request.POST)
        if signup_form.is_valid():
            signup_form.save()
            username = signup_form.cleaned_data.get('username')
            raw_password = signup_form.cleaned_data.get('password1')
            user = authenticate(username=username, password=raw_password)
            return redirect('signup_confirm')
    else:
        signup_form = SignUpForm()

    context = {
        'signup_form': signup_form,
    }
    return render(request, 'fostania_web_app/signup.html', context)


def signup_confirm(request):
    return render(request, 'fostania_web_app/signup_confirm.html')

forms.py中的注册表单:

class SignUpForm(UserCreationForm):
    email = forms.CharField(required=True, help_text='البريد الإلكترونى الخاص بك - يجب ان يكون حقيقى (يستخدم لتسجيل الدخول) ')
    name = forms.CharField(required=True, help_text='إسمك الحقيقى -  سيظهر كأسم البائع')
    password1 = forms.CharField(widget=forms.PasswordInput,
                                help_text='كلمة المرور - حاول ان تكون سهلة التذكر بالنسبة لك')
    password2 = forms.CharField(widget=forms.PasswordInput,
                                help_text='تأكيد كلمة المرور - إكتب نفس كلمة المرور السابقة مرة أخرى')

    class Meta:
        model = UserModel
        fields = ('email','name',  'password1', 'password2', )
        labels = {
            'name': 'إسمك الحقيقى -  سيظهر كأسم البائع',
            'email': 'البربد الإلكترونى Email',
            'password1': 'كلمة المرور',
            'password2': 'تأكيد كلمة المرور'
        }

我现在需要的只是让它忽略区分大小写。

更新

这是我的登录文件

网址.py

   path('login/', auth_views.login, name='login'),

注册/login.html

    <form method="post">
    {% csrf_token %}
    البريد الإلكترونى E-Mail &nbsp;<Br>{{ form.username }}
                    <br><br>
                     كلمة المرور &nbsp;<Br>{{ form.password }}
                    <Br><br>
      <div align="center">
    <button class ="btn btn-primary" submit>تسجيل الدخول</button><br><br>
      <a href="{% url 'signup' %}"><button type="button" class="btn btn-warning">
          إنشاء حساب جديد</button></a>
          </div>

  </form>
4

4 回答 4

13

如果您不介意丢失用户输入电子邮件的格式,则更简洁的方法可能是覆盖模型字段本身。

这对我来说效果更好,因为我只需要在一个地方进行更改。否则,您可能不得不担心注册、登录、用户更新、API 视图等。

您必须覆盖的唯一方法是to_python. UserModel.email这只会将保存到该字段的任何内容小写。

from django.db import models


class LowercaseEmailField(models.EmailField):
    """
    Override EmailField to convert emails to lowercase before saving.
    """
    def to_python(self, value):
        """
        Convert email to lowercase.
        """
        value = super(LowercaseEmailField, self).to_python(value)
        # Value can be None so check that it's a string before lowercasing.
        if isinstance(value, str):
            return value.lower()
        return value

您的用户模型将只是..


# Assuming you saved the above in the same directory in a file called model_fields.py
from .model_fields import LowercaseEmailField

class UserModel(AbstractBaseUser, PermissionsMixin):
    email = LowercaseEmailField(unique=True)
    # other stuff...
于 2019-10-22T00:37:27.967 回答
6

您无需进行太多更改即可完成此操作 - 在您的情况下,您只需要更改表单并使用 Django 的内置表单数据清理器或创建自定义字段

您应该使用EmailField而不是 aCharField进行内置验证。此外,您没有发布您的 AuthenticationForm,但我认为您已将其更改为包含email而不是username.

使用数据清理器:

class SignUpForm(UserCreationForm):
    # your code
    email = forms.EmailField(required=True)
    def clean_email(self):
        data = self.cleaned_data['email']
        return data.lower()

class AuthenticationForm(forms.Form):
    # your code
    email = forms.EmailField(required=True)
    def clean_email(self):
        data = self.cleaned_data['email']
        return data.lower()

使用自定义字段:

class EmailLowerField(forms.EmailField):
    def to_python(self, value):
        return value.lower()

class SignUpForm(UserCreationForm):
    # your code
    email = EmailLowerField(required=True)

class AuthenticationForm(forms.Form):
    # your code
    email = EmailLowerField(required=True)

这样,您可以确保每封电子邮件都以小写形式保存到您的数据库中,并且对于每次登录尝试,电子邮件在与数据库值相比之前都是小写的。

于 2018-06-17T10:33:18.677 回答
2

创建帐户条目时,将电子邮件解释为email.lower()在您的数据库中创建规范化条目。

从登录表单解析电子邮件/用户名时,您还应该使用它email.lower()来匹配数据库条目。


注意:normalize_email唯一清理电子邮件地址的域部分:

classmethod normalize_email(email)

通过小写电子邮件地址的域部分来规范化电子邮件地址。

于 2018-06-17T10:27:26.477 回答
1

对于使用Postgres和 django 2 或更高版本的任何人:

有一个CIEmailField存储和检索电子邮件地址区分大小写但比较它不区分大小写。这样做的好处是在地址中保留大小写(如果用户希望他们的地址看起来像JohnDoe@ImportantCompany.com),而不允许多个帐户使用相同的电子邮件。

用法很简单:

from django.contrib.postgres.fields import CIEmailField
# ...
class User(AbstractBaseUser, ...):
    email = CIEmailField(unique=True, ...)
    # ...

除了迁移我得到了错误:

django.db.utils.ProgrammingError: type "citext" does not exist

要解决这个问题,请按照这个答案CITextExtension在第一次CreateModel迁移操作之前设置:

from django.contrib.postgres.operations import CITextExtension

class Migration(migrations.Migration):
    ...

    operations = [
        CITextExtension(),
        ...
    ]

注意:在 so 答案和官方文档中都规定您应该在第一次 CreateModel 迁移操作之前添加它。对我来说,在使用CIEmailField.

于 2021-09-14T09:22:47.000 回答