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

DjangoDjango ModelsDjango Authentication

Django Problem Overview


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.

Django Solutions


Solution 1 - Django

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

Solution 2 - Django

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

Solution 3 - Django

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()

Solution 4 - Django

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))

Solution 5 - Django

As far as I know one can override user model since Django 1.5 which will solve a problem. Simple example here

Solution 6 - Django

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.

Solution 7 - Django

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:

  1. drop the auth_user table
  2. change username to max_length=75 in the model
  3. 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:

  1. backup the user table data
  2. drop the table
  3. syncdb
  4. 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:

  1. create your custom model and temporarily stand it alongside the default model
  2. write a script which copies the instances from the default model to the new model
  3. replace the default model with your custom one

The latter is not as hard as it sounds, but obviously involves a bit more work.

Solution 8 - Django

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 https://stackoverflow.com/questions/1199190/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.

Solution 9 - Django

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

Solution 10 - Django

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

Solution 11 - Django

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'

Solution 12 - Django

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.

Solution 13 - Django

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPurrellView Question on Stackoverflow
Solution 1 - DjangoClémentView Answer on Stackoverflow
Solution 2 - DjangoskoczenView Answer on Stackoverflow
Solution 3 - DjangoRostView Answer on Stackoverflow
Solution 4 - DjangoCerinView Answer on Stackoverflow
Solution 5 - DjangoDmytriy VoloshynView Answer on Stackoverflow
Solution 6 - DjangoBartekView Answer on Stackoverflow
Solution 7 - DjangoJohn MeeView Answer on Stackoverflow
Solution 8 - DjangoGreg GlocknerView Answer on Stackoverflow
Solution 9 - DjangoxiaowlView Answer on Stackoverflow
Solution 10 - DjangoРустам ЗайнуллинView Answer on Stackoverflow
Solution 11 - DjangoCraschedView Answer on Stackoverflow
Solution 12 - DjangohaiptView Answer on Stackoverflow
Solution 13 - DjangolprsdView Answer on Stackoverflow