Django authentication with long usernames - django

In the models.py of django/django/contrib/auth the username is length is changed to 64 fields instead of 30 and the username in database has varchar(64).But it doesnt allow to login with a long username like harry#sdsdasfdfdfgdfgdfgdfgdfgdfg.com
how can this be fixed
class User(models.Model):
"""
Users within the Django authentication system are represented by this model.
Username and password are required. Other fields are optional.
"""
username = models.CharField(_('username'), max_length=64, unique=True, help_text=_("Required. 64 characters or fewer. Letters, numbers and #/./+/-/_ characters"))

I would recommend that you not monkey patch Django, but instead create your own authentication backend. You can then validate against a user's email like in this example: http://djangosnippets.org/snippets/1845/

I know this is an old question, but I feel it's time for a new answer since custom User models is arguably the best feature of django 1.5.
From now on one can create his own custom User model and use it along with existing authentication functionality. The latest documentation covers it all in detail here https://docs.djangoproject.com/en/dev/topics/auth/#auth-custom-user.

Hey buddy there is a nice application named django-registration in the bitbucket, i m sure u will get a possible way to accomplish your task. Please let me know if this link helps you out. http://bitbucket.org/ubernostrum/django-registration

Related

User registration with admin authorization

I was wonder if it is possible to include a way that when someone fill the user registration form to register, can the details be sent to an admin email for authorization before the user can login in django?
Since you did not provide any code I will guide you the process, you can later come back more specific question if you are stuck :
Use the field is_active provided by Django from the User model to authorised access within your website.
Extends the field is_active to set the default to False or set it to false in the begging of your user view
Create a link with the ID of the user and a path to the Django Admin where you can update the user and active to True
In short yes, possible and pretty easy if you know a bit of Django.

Django 2 login with email address, phone number or username

What is the best way to implement django 2 login and registration like instagram.com. user can register with username and email or phone and password. after register user can login with email address, phone number or username.
Thanks
A great thing about Django is that it comes with a User model already. You just have to apply it to your site.
Check out the user documentation Here
This will give you all the fields you can use, and how to make a member style website

Email as username in Django

Okay, this one is pretty obvious to everyone who use Django and frequently asked by newbies, but I'd like to make it clear and discuss if there are any other ways to do it. The most widespread and convenient approach now is to store email in username field as Django 1.2 allows "#", "_" and "-" characters, but this way has following issues:
The worst one: username field is restricted by max_length=30 property, which is ridiculously small for emails. Even if you override form validation, DB will have varchar(30) instead of EmailField's varchar(75) unless you alter your table manually.
You need to store your email data both in username and email field to make User.email_user() working. I think there are some other places when User.email is used.
Code readability fail. Sure, other djangonauts know about this pitfall, but treating field called 'username' (especially when there is still email field) as email obviously makes your code less understandable.
The other approach could be authentication using email field by passing it to your auth backend like so, but it still has problems:
authenticate(self, email=None, password=None)
User.email doesn't have unique=True property, which means that your DB won't have index, making your lookups by email slow like hell.
You have to deal with username field, which has unique=True, by completely removing it from your table or altering it to allow NULL and removing index.
Resuming, both ways are evil and require DB-specific code to be executed after syncdb, which is unacceptable if you need DB-independent application.
I've packaged up django-email-as-username which should pretty much do everything you need if you're looking to remove usernames, and only use emails.
The brief overview is:
Provides an email auth backend and helper functions for creating users.
Patches the Django admin to handle email based user authentication.
Overides the createsuperuser command to create users with email only.
Treats email authentication as case-insensitive.
Under the hood usernames are hashed versions of the emails, which ends up meaning we're not limited to the Django's username 30 char limit (Just the regular email 75 char limit.)
Edit: As of Django 1.5, you should look into using a custom User model instead of the 'django-email-as-username' package.
David Cramer came up with a solution to this problem that I love. I'm currently using it on a production site where the user has to be able to log in using their email OR their username. You can find it here:
Logging In With Email Addresses in Django
If the login name provided on the form is an email (contains the '#' symbol), it will attempt to authenticate with that, and will fall back on the username if it isn't an email. (Naturally, you just need to make sure your registration form captures an email for this work.)
Well, I haven't had to use emails as usernames in Django but I guess You could create a UserProfile model and aggregate fields to it, like another email field and make it unique. So you could do user.get_profile().email for your authentication.
I guess other way to go would be to inherit User and redefine the fields, but I think this still not recommended by Django developers.
Finally you could define your own custom User model and back on the django.contrib.auth.models.User for some logic.
Code to alter User table within Django:
from django.db import connection
cursor = connection.cursor()
cursor.execute("ALTER TABLE auth_user MODIFY COLUMN username varchar(75) NOT NULL")

Can django's auth_user.username be varchar(75)? How could that be done?

Is there anything wrong with running alter table on auth_user to make username be varchar(75) so it can fit an email? What does that break if anything?
If you were to change auth_user.username to be varchar(75) where would you need to modify django? Is it simply a matter of changing 30 to 75 in the source code?
username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and #/./+/-/_ characters"))
Or is there other validation on this field that would have to be changed or any other repercussions to doing so?
See comment discussion with bartek below regarding the reason for doing it.
Edit: Looking back on this after many months. For anyone who doesn't know the premise: Some apps don't have a requirement or desire to use a username, they use only email for registration & auth. Unfortunately in django auth.contrib, username is required. You could start putting emails in the username field, but the field is only 30 char and emails may be long in the real world. Potentially even longer than the 75 char suggested here, but 75 char accommodates most sane email addresses. The question is aimed at this situation, as encountered by email-auth-based applications.
There's a way to achieve that without touching the core model, and without inheritance, but it's definitely hackish and I would use it with extra care.
If you look at Django's doc on signals, you'll see there's one called class_prepared, which is basically sent once any actual model class has been created by the metaclass. That moment is your last chance of modifying any model before any magic takes place (ie: ModelForm, ModelAdmin, syncdb, etc...).
So the plan is simple, you just register that signal with a handler that will detect when it is called for the User model, and then change the max_length property of the username field.
Now the question is, where should this code lives? It has to be executed before the User model is loaded, so that often means very early. Unfortunately, you can't (django 1.1.1, haven't check with another version) put that in settings because importing signals there will break things.
A better choice would be to put it in a dummy app's models module, and to put that app on top of the INSTALLED_APPS list/tuple (so it gets imported before anything else). Here is an example of what you can have in myhackishfix_app/models.py :
from django.db.models.signals import class_prepared
def longer_username(sender, *args, **kwargs):
# You can't just do `if sender == django.contrib.auth.models.User`
# because you would have to import the model
# You have to test using __name__ and __module__
if sender.__name__ == "User" and sender.__module__ == "django.contrib.auth.models":
sender._meta.get_field("username").max_length = 75
class_prepared.connect(longer_username)
That will do the trick.
A few notes though:
You might want to change also the help_text of the field, to reflect the new maximum length
If you want to use the automatic admin, you will have to subclass UserChangeForm, UserCreationForm and AuthenticationForm as the maximum length is not deduced from the model field, but directly in the form field declaration.
If you're using South, you can create the following migration to change the column in the underlying database:
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Changing field 'User.username'
db.alter_column('auth_user', 'username', models.CharField(max_length=75))
def backwards(self, orm):
# Changing field 'User.username'
db.alter_column('auth_user', 'username', models.CharField(max_length=35))
models = {
# ... Copy the remainder of the file from the previous migration, being sure
# to change the value for auth.user / usename / maxlength
Based on Clément and Matt Miller's great combined answer above, I've pulled together a quick app that implements it. Pip install, migrate, and go. Would put this as a comment, but don't have the cred yet!
https://github.com/GoodCloud/django-longer-username
EDIT 2014-12-08
The above module is now deprecated in favor of https://github.com/madssj/django-longer-username-and-email
Updated solution for the Django 1.3 version (without modifying manage.py):
Create new django-app:
monkey_patch/
__init__.py
models.py
Install it as first: (settings.py)
INSTALLED_APPS = (
'monkey_patch',
#...
)
Here is models.py:
from django.contrib.auth.models import User
from django.core.validators import MaxLengthValidator
NEW_USERNAME_LENGTH = 300
def monkey_patch_username():
username = User._meta.get_field("username")
username.max_length = NEW_USERNAME_LENGTH
for v in username.validators:
if isinstance(v, MaxLengthValidator):
v.limit_value = NEW_USERNAME_LENGTH
monkey_patch_username()
The solutions above do seem to update the model length. However, to reflect your custom length in admin, you also need to override the admin forms (frustratingly, they don't simply inherit the length from the model).
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
UserChangeForm.base_fields['username'].max_length = NEW_USERNAME_LENGTH
UserChangeForm.base_fields['username'].widget.attrs['maxlength'] = NEW_USERNAME_LENGTH
UserChangeForm.base_fields['username'].validators[0].limit_value = NEW_USERNAME_LENGTH
UserChangeForm.base_fields['username'].help_text = UserChangeForm.base_fields['username'].help_text.replace('30', str(NEW_USERNAME_LENGTH))
UserCreationForm.base_fields['username'].max_length = NEW_USERNAME_LENGTH
UserCreationForm.base_fields['username'].widget.attrs['maxlength'] = NEW_USERNAME_LENGTH
UserCreationForm.base_fields['username'].validators[0].limit_value = NEW_USERNAME_LENGTH
UserCreationForm.base_fields['username'].help_text = UserChangeForm.base_fields['username'].help_text.replace('30', str(NEW_USERNAME_LENGTH))
As far as I know one can override user model since Django 1.5 which will solve a problem. Simple example here
If you simply modify the database table, you'll still have to deal with Django's validation, so it won't let you make one over 30 characters anyways. Additionally, the username validates so that it can't have special characters like # so simply modifying the length of the field wouldn't work anyways. My bad, looks like it handles that. Here's the username field from models.py in django.contrib.auth:
username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and #/./+/-/_ characters"))
Creating email auth is not hard. Here's a super simple email auth backend you can use. Al l you need to do after that is add some validation to ensure the email address is unique and you're done. That easy.
Yes, it can be done. At least I think this should work; I wound up replacing the whole auth model, so am ready to be corrected if this doesn't work out...
If you have no user records you care about:
drop the auth_user table
change username to max_length=75 in the model
syncdb
If you have user records you need to retain then it's more complicated as you need to migrate them somehow. Easiest is backup and restore of the data from old to new table, something like:
backup the user table data
drop the table
syncdb
reimport user data to the new table; taking care to restore the original id values
Alternatively, using your mad python-django skillz, copy the user model instances from old to new and replace:
create your custom model and temporarily stand it alongside the default model
write a script which copies the instances from the default model to the new model
replace the default model with your custom one
The latter is not as hard as it sounds, but obviously involves a bit more work.
Fundamentally, the problem is that some people want to use an email address as the unique identifier, while the user authentication system in Django requires a unique username of at most 30 characters. Perhaps that will change in the future, but that's the case with Django 1.3 as I'm writing.
We know that 30 characters is too short for many email addresses; even 75 characters is not enough to represent some email addresses, as explained in What is the optimal length for an email address in a database?.
I like simple solutions, so I recommend hashing the email address into a username that fits the restrictions for usernames in Django. According to User authentication in Django, a username must be at most 30 characters, consisting of alphanumeric characters and _, #, +, . and -. Thus, if we use base-64 encoding with careful substitution of the special characters, we have up to 180 bits. So we can use a 160-bit hash function like SHA-1 as follows:
import hashlib
import base64
def hash_user(email_address):
"""Create a username from an email address"""
hash = hashlib.sha1(email_address).digest()
return base64.b64encode(hash, '_.').replace('=', '')
In short, this function associates a username for any email address. I'm aware that there is a tiny probability of a collision in the hash function, but this should not be an issue in most applications.
Just adding the below code at the bottom of settings.py
from django.contrib.auth.models import User
User._meta.get_field("username").max_length = 75
C:...\venv\Lib\site-packages\django\contrib\auth\models.py
first_name = models.CharField(_('first name'), max_length=30, blank=True)
change to
first_name = models.CharField(_('first name'), max_length=75, blank=True)
save
and change in the database
I am using django 1.4.3 which makes it pretty easy and I did not have to change anything else in my code after realising I wanted to use long email addresses as usernames.
If you have direct access to the database, change it there to the amount of characters you would like to, in my case 100 characters.
In your app model (myapp/models.py) add the following
from django.contrib.auth.models import User
class UserProfile(models.Model):
# This field is required.
User._meta.get_field("username").max_length = 100
user = models.OneToOneField(User)
Then in your settings.py you specify the model:
AUTH_USER_MODEL = 'myapp.UserProfile'
If you are using venv (virtual environment), the simplest solution probably is just update the core code directly, i.e. opening the following two files:
- - venv/lib/python2.7/sites-packages/django/contrib/auth/model.py
- venv/lib/python2.7/sites-packages/django/contrib/auth/forms.py
Search for all username field and change max_length from 30 to 100. It is safe since you are already using venv so it won't affect any other Django project.
The best solution is to use email field for email and the username for username.
In the input login form validation, find whether the data is username or the email and if email, query the email field.
This only requires monkey patching the contrib.auth.forms.login_form which is a few lines in the corresponding view.
And it is far better than trying to modify the models and the database tables.

Anyone think django's user model is too tightly coupled with auth?

I'm trying to learn Django and I would like feedback from anyone who has any MVC/MTV/PHP/Ruby framework experience. Does anyone find that the user model is too tightly coupled with auth?
Background: When you first implement authentication for Django, you include the module django.contrib.auth
This will bring in several models like User, Group, Message etc. Let's focus on the User model as this is the one of the most important tables in any website.
In short the User table has these fields
User
username max_length 30, unique, [letters, digits, underscores]
password max_length 75
email max_length 75
...and about 8 other useful fields like first_name, last_name, etc.
Goal:
I want to remove username and use email as the login for every user. It's a pretty simple request that many websites use these days.
I don't want to monkey patch the core code since this will make upgrading more difficult later on. This means modifying the User model is out of the question. I only want to do a few simple and basic things I expect a few frameworks to do so let me address how Django does it.
Adding new fields to the User model
Django docs says to use create another table and insert the fields there. You will have a one to one relationship between the User table and the Profile table.
eg.
If You want to add an image field to each user you add it to the profile table. A join query is made every single time. They've even specified a constant to tell the framework what table to use:
AUTH_PROFILE_MODULE = 'accounts.UserProfile'
I don't think it's the best practice to have to do a join query every time I want a field that should belong to the user table.
Another option is to use the function add_to_class.
The django community has stated it's not good to define new fields outside of the main class because other developers who add methods won't know all the data members.
Editing old fields
The auth module does a check against two fields username and the hashed password. Looking at the above table I would need to change the username model to accept these properties. Length of 75 with all the valid characters of the email. The django suggests I check against the email field.
Two problems arise if I use the email field to auth against:
I need to write a new class to be used in a constant AUTHENTICATION_BACKEND, so it checks against the email field and I have an unused field called username.
Adding new methods
In MVC/MTV a design principle is to use fat models skinny controllers. Since the model is declared in auth, I'm not sure how one is supposed to add methods that act on the user model's fields. Since django suggests using a Profile model, I suppose they will have to go there.
Extending the User class
A small annoyance would be that I can't use the name 'User' and instead must use 'Users' or 'Accounts'. A bigger one is I don't think the auth would recognize this new module. Meaning I would have to rewrite a bunch functionality that is is present. This one doesn't bother me as it's something I expect to do in other frameworks.
Any comments are appreciated. I wouldn't ask all these questions and look for solutions if I wasn't truly interested in using django.
I agree that django's incessant clinginess to the auth models is absurd. My job requires me to create ultra scalable and very high load sites which sometimes require user authentication and djano's auth model + permissions does not fit with that.
Fortunately, it's not difficult to replace.
First, create a custom User model.
class User(models.Model):
...fields...
#Define some interface methods to be compatible.
def get_and_delete_messages(self):
def is_active(self):
def is_anonymous(self):
def is_authenticated(self):
def is_staff(self):
def has_perm(self, perm_list):
Second, create your own authentication back-end.
class LocalAccount(object):
"""
This checks our local user DB for authentication
"""
def authenticate(self, username=None, password=None):
try:
user = User.objects.get(alias=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.select_related().get(pk=user_id)
except User.DoesNotExist:
return None
#settings.py
AUTHENTICATION_BACKENDS = (
'helpers.auth.LocalAccount',
)
That should solve most of your issues, I don't even think all of the methods you would find on django.contrib.auth.User are necessary, I recommend trying it out.
The one gotcha here is that the admin may start to bitch, fortunately that's really easy to patch using simple python inheritance as well. That's another question though :)
At the end of the day your project's auth backend needs some sort of store for auth credentials. That the default auth backend is tightly coupled to the User model is not strange in this respect. It's easy enough to substitute your own definition for the user model if you write your own auth backend, as I have in the past.
I created my Profile model and use AUTH_PROFILE_MODULE, so I have complete control over my model, I can modify fields, add methods, etc. Now I'm thinking about using cache and writing middleware that will get profile from cache if possible.
To login using email you could write very simple auth backend:
from django.contrib.auth.models import User
from django.contrib.auth.backends import ModelBackend
class EmailModelBackend(ModelBackend):
def authenticate(self, username=None, password=None):
try:
user = User.objects.get(email=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None