Django : Case Insensitive Matching Of Username From Auth User?
Solution 1:
As of Django 1.5, making usernames case insensitive is straightforward:
classMyUserManager(BaseUserManager):defget_by_natural_key(self, username):
returnself.get(username__iexact=username)
Solution 2:
I modified few lines in my registration and login process that seems to work for me. With my solution usernames will still be displayed like the user wrote them when registering, but it will not allow others to use the same username written differently. It also allows users to login without worrying about writing the case sensitive username.
I modified the registration form to search for case insensitive usernames.
This is line from my validation of username, it searches for user with this username.
User._default_manager.get(username__iexact=username)
Then I needed to allow users to login with case insensitive usernames.
From my login view:
username = request.POST['username']
password = request.POST['password']
caseSensitiveUsername = username
try:
findUser = User._default_manager.get(username__iexact=username)
except User.DoesNotExist:
findUser = Noneif findUser isnotNone:
caseSensitiveUsername = findUser.get_username
user = auth.authenticate(username=caseSensitiveUsername, password=password)
Solution 3:
Finally got it :
With so much experimenting and minimum effect on User
model, finally achieved it.
[ Thanks to Mr. @freakish for a different thought ]
Here it is :
############ username case-insensitivity ############classiunicode(unicode):
def__init__(self, value):
super(iunicode, self).__init__(value)
self.value = value
def__eq__(self, other):
ifisinstance(other, str) orisinstance(other, unicode):
return self.value.lower() == other.lower()
ifisinstance(other, self.__class__):
return other == self.value
defcustom_getattribute(self, name):
val = object.__getattribute__(self, name)
if name == "username":
val = iunicode(val)
return val
defauth_user_save(self, *args, **kwargs): # Ensures lowercase usernames
username = self.username
if username andtype(username) in [unicode, str, iunicode]:
self.username = username.lower() # Only lower case allowedsuper(User, self).save(*args, **kwargs)
User.__getattribute__ = custom_getattribute
User.save = MethodType(auth_user_save, None, User)
#####################################################
I tested it and it worked as expected. :D
So, here are the testcases :
from django.test.testcases import TestCase
defcreate_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
classUsernameCaseInsensitiveTests(TestCase):
deftest_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)
deftest_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()))
Implicit Case-insensitive queries for database
###################### 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
#############################################################
This will allow, User.objects.get(username='yugal')
& User.objects.get(username='YUGAl')
yield the same user.
Solution 4:
The simplest way to use case insensitive username is to inherit from default ModelBackend
and override authenticate
method.
Please note that inside the except
block we are executing UserModel().set_password(password)
, by doing this we decreasing hasher work time. Fixed bug report
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from users.models import User
classCaseInsensitiveModelBackend(ModelBackend):
defauthenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
if username isNone:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
d = {'%s__iexact'%UserModel.USERNAME_FIELD: username}
user = UserModel.objects.get(**d)
if user.check_password(password):
return user
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
returnNone
And add this backend to AUTHENTICATION_BACKENDS
in settings.py
AUTHENTICATION_BACKENDS = (
'sdcpy.backends.CaseInsensitiveModelBackend', # inherits from'django.contrib.auth.backends.ModelBackend'
)
Solution 5:
This monkey patching looks like a bad idea. You will definetly hit some problems in future ( Django does a lot of stuff behind the scene ). I highly recommend redesigning your app.
However here's what you can try ( using your iUnicode
class ):
def new_getattribute( self, name ):
val = object.__getattribute__( self, name )
if name == "username":
val = iUnicode( val )
returnval
User.__getattribute__ = new_getattr
Now, I'm not 100% that this will work, and it is a bit hacky, so use it with caution. :)
Post a Comment for "Django : Case Insensitive Matching Of Username From Auth User?"