Django by-default implements username as case sensitive, now for authentication I have written my own Authentication Backend
to handle case insensitive usernames while authentication.
As shown in : http://blog.shopfiber.com/?p=220
Now, the problem is :
I have various views and util methods which compares username
to some stings.
i.e.
request.user.username == username_from_some_other_system_as_str
Now, if username is yugal
then:
request.user.username == 'Yugal' # Returns False
Now, it should return True
[ What I wanted to achieve ]
For that I remember from C++
days, Operator Overloading
.
But I don't think simply doing that for django's auth user
would be a good idea, since auth user
is tightly bound with django
.
Also, overloading ==
will make it case-insensitive for the whole class not just for the username
field.
So, how should I go about this username
case-insensitivity even when compared throughout.
Note:
Creating a get_username
method that returns lower-case username always is not possible, since it would require all code to be re-factored to use it. You can do it for your code for once, but not possible if you are using 3rd party django apps.
I know user.username.lower() = something.lower()
is possible but is bug prone and not the write solution for something so often used in a multi-developer setup.
I have used SomeModel.objects.filter(username__iexact=username)
, wherever possible. But that still leaves the system vulnerable to a mistake by any of un-aware developer.
======================================
Figured out the solution conceptually, but could not make it work ( Help ) :
####### 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
################################################################
I assume to_python
is used to convert the value received from database to unicode
in python. But, I guess my to_python
is not getting called.
This will also ensure case-insensitivity in 3rd party apps and would not require any re-factoring. It will patch the
User
at its core. I will be adding this to__init__.py
of my firstINSTALLED_APP
What am I doing wrong ?
By default, Django does a case-sensitive check on usernames.
If you want to use django's default authentication backend you cannot make username non unique. You will have to implement a class with get_user(user_id) and authenticate(request, **credentials) methods for a custom backend.
As of Django 1.5, making usernames case insensitive is straightforward:
class MyUserManager(BaseUserManager):
def get_by_natural_key(self, username):
return self.get(username__iexact=username)
Sources: 1, 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 = None
if findUser is not None:
caseSensitiveUsername = findUser.get_username
user = auth.authenticate(username=caseSensitiveUsername, password=password)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With