Django:来自auth用户的用户名不区分大小写?

Yug*_*dle 13 python django operator-overloading

Django默认情况下将用户名视为区分大小写,现在用于身份验证我已经编写了自己的Authentication Backend用户名,以便在身份验证时处理不区分大小写的用户名.

如下所示:http://blog.shopfiber.com/?p = 220

现在,问题是:

我有各种各样的观点和实用方法,可以比较username一些刺痛.

request.user.username == username_from_some_other_system_as_str
Run Code Online (Sandbox Code Playgroud)

现在,如果用户名是yugal:

request.user.username == 'Yugal' # Returns False
Run Code Online (Sandbox Code Playgroud)

现在,它应该返回True[我想要实现的目标]

为此,我记得C++几天Operator Overloading.但我不认为简单地为django做那个auth user将是一个好主意,因为它auth user是紧密结合的django.此外,重载==将使整个类不仅对username字段不区分大小写.

那么,username即使在整个过程中进行比较,我应该如何处理这种不区分大小写的问题.

注意:

  • get_username始终无法创建返回小写用户名的方法,因为它需要重新考虑所有代码才能使用它.您可以为您的代码执行一次,但如果您使用的是第三方django应用程序则无法实现.

  • 我知道这user.username.lower() = something.lower()是可能的,但是容易出错,而不是针对多开发人员设置中经常使用的东西的写入解决方案.

  • SomeModel.objects.filter(username__iexact=username)尽可能地使用过.但这仍然让系统容易受到任何不知情的开发人员的错误的影响.

======================================

从概念上找出解决方案,但无法使其工作(帮助):

####### Custom CharField for username case-insensitivity #######
from django.db.models.fields import CharField
class iUnicode:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        if isinstance(other, str) or isinstance(other, unicode):
            return self.value.lower() == other.lower()
        if isinstance(other, self.__class__):
            return other == self.value

    def __unicode__(self):
        return unicode(self.value)
    def __str__(self):
        return self.__unicode__()


class UsernameCharField(CharField):
    def to_python(self, value):  # Its not getting called
        unicode_val = super(CharField, self).to_python(value)
        return iUnicode(unicode_val)

if User._meta.local_fields[1].name == 'username':
    User._meta.local_fields[1] = UsernameCharField(max_length=30)
    User._meta.local_fields[1].model = User
################################################################
Run Code Online (Sandbox Code Playgroud)

我假设to_python用于将从数据库接收的值转换为unicodepython.但是,我猜我to_python没有被召唤.

这还将确保第三方应用程序中的大小写不敏感,并且不需要任何重新分解.它将修补User其核心.我将在__init__.py第一次添加此内容INSTALLED_APP

我究竟做错了什么 ?

Jos*_*ley 26

从Django 1.5开始,使用户名不敏感是很简单的:

class MyUserManager(BaseUserManager):
    def get_by_natural_key(self, username):
        return self.get(username__iexact=username)
Run Code Online (Sandbox Code Playgroud)

来源:1,2

  • 或者,如果您想观察自定义[USERNAME_FIELD](https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#django.contrib.auth.models.CustomUser.USERNAME_FIELD),请更改最后一个line to:`return self.get(**{self.model.USERNAME_FIELD +'__ iexact':username})` (4认同)

Maz*_*vél 6

我在注册和登录过程中修改了几行似乎对我有用.使用我的解决方案,用户名仍将显示为用户在注册时编写的用户名,但不允许其他人使用不同的用户名.它还允许用户登录而无需担心编写区分大小写的用户名.

我修改了注册表单以搜索不区分大小写的用户名.

这是我对用户名的验证,它使用此用户名搜索用户.

User._default_manager.get(username__iexact=username)
Run Code Online (Sandbox Code Playgroud)

然后我需要允许用户使用不区分大小写的用户名登录.

从我的登录视图:

username = request.POST['username']
password = request.POST['password']
caseSensitiveUsername = username
try:
  findUser = User._default_manager.get(username__iexact=username)
except User.DoesNotExist:
  findUser = None
if findUser is not None:
  caseSensitiveUsername = findUser.get_username
user = auth.authenticate(username=caseSensitiveUsername, password=password)
Run Code Online (Sandbox Code Playgroud)


Yug*_*dle 4

终于明白了:

经过如此多的实验和对User模型的最小影响,终于实现了。[感谢@freakish先生的不同想法]

这里是 :

############ username case-insensitivity ############
class iunicode(unicode):
    def __init__(self, value):
        super(iunicode, self).__init__(value)
        self.value = value

    def __eq__(self, other):
        if isinstance(other, str) or isinstance(other, unicode):
            return self.value.lower() == other.lower()
        if isinstance(other, self.__class__):
            return other == self.value


def custom_getattribute(self, name):
    val = object.__getattribute__(self, name)
    if name == "username":
        val = iunicode(val)
    return val

def auth_user_save(self, *args, **kwargs): # Ensures lowercase usernames
    username = self.username
    if username and type(username) in [unicode, str, iunicode]:
        self.username = username.lower()   # Only lower case allowed
    super(User, self).save(*args, **kwargs)

User.__getattribute__ = custom_getattribute
User.save = MethodType(auth_user_save, None, User)
#####################################################
Run Code Online (Sandbox Code Playgroud)

我测试了它,它按预期工作。:D

所以,这是测试用例

from django.test.testcases import TestCase

def create_user(data='testuser'):
    email = '%s@%s.com' % (data, data)
    user = G(User, username=data, email=email, is_active=True)
    user.set_password(data)
    user.save()
    return user

class UsernameCaseInsensitiveTests(TestCase):

    def test_user_create(self):
        testuser = 'testuser'
        user = create_user(testuser)
        # Lowercase
        self.assertEqual(testuser, user.username)
        # Uppercase
        user.username = testuser.upper()
        user.save()
        self.assertEqual(testuser, user.username)

def test_username_eq(self):
    testuser = 'testuser'
    user = create_user(testuser)
    self.assertTrue(isinstance(user.username, iunicode))
    self.assertEqual(user.username, testuser)
    self.assertEqual(user.username, testuser.upper())
    self.assertTrue(user.username == testuser.upper())
    self.assertTrue(testuser.upper() == user.username)
    self.assertTrue(user.username == iunicode(testuser.upper()))
Run Code Online (Sandbox Code Playgroud) 对数据库的隐式不区分大小写的查询
###################### QuerySet #############################
def _filter_or_exclude(self, negate, *args, **kwargs):
    if 'username' in kwargs:
        kwargs['username__iexact'] = kwargs['username']
        del kwargs['username']
    if args or kwargs:
        assert self.query.can_filter(),\
        "Cannot filter a query once a slice has been taken."
    from django.db.models import Q
    clone = self._clone()
    if negate:
        clone.query.add_q(~Q(*args, **kwargs))
    else:
        clone.query.add_q(Q(*args, **kwargs))
    return clone

from django.db.models.query import QuerySet
QuerySet._filter_or_exclude = _filter_or_exclude
#############################################################
Run Code Online (Sandbox Code Playgroud)

这将允许User.objects.get(username='yugal')User.objects.get(username='YUGAl')产生相同的用户。