78👍
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
andAuthenticationForm
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
25👍
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
- [Django]-Django JSONField inside ArrayField
- [Django]-What is the difference between {% load staticfiles %} and {% load static %}
- [Django]-How do I perform query filtering in django templates
21👍
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()
- [Django]-Django rest framework nested self-referential objects
- [Django]-Django "login() takes exactly 1 argument (2 given)" error
- [Django]-Django's timezone.now does not show the right time
5👍
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))
- [Django]-Proper way to handle multiple forms on one page in Django
- [Django]-How do I use a dictionary to update fields in Django models?
- [Django]-Unit testing with django-celery?
2👍
As far as I know one can override user model since Django 1.5 which will solve a problem. Simple example here
- [Django]-How can I set a default value for a field in a Django model?
- [Django]-How do you log server errors on django sites
- [Django]-Django custom field validator vs. clean
1👍
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 My bad, looks like it handles that. Here’s the username field from models.py in django.contrib.auth:@
so simply modifying the length of the field wouldn’t work anyways.
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.
- [Django]-How to store a dictionary on a Django Model?
- [Django]-Django custom field validator vs. clean
- [Django]-OperationalError, no such column. Django
1👍
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.
- [Django]-Django's forms.Form vs forms.ModelForm
- [Django]-What's the best Django search app?
- [Django]-How do you limit list objects template side, rather than view side
1👍
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.
- [Django]-Get object by field other than primary key
- [Django]-Django @login_required decorator for a superuser
- [Django]-Where is a good place to work on accounts/profile in Django with the Django registration app?
0👍
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
- [Django]-How do you use the django-filter package with a list of parameters?
- [Django]-Override existing Django Template Tags
- [Django]-Django rest framework lookup_field through OneToOneField
0👍
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
- [Django]-AssertionError: database connection isn't set to UTC
- [Django]-Error 111 connecting to localhost:6379. Connection refused. Django Heroku
- [Django]-Django admin TabularInline – is there a good way of adding a custom html column?
-1👍
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'
- [Django]-Is virtualenv recommended for django production server?
- [Django]-How to paginate Django with other get variables?
- [Django]-Django optional URL parameters
-2👍
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.
- [Django]-Django TemplateDoesNotExist?
- [Django]-How do I make an auto increment integer field in Django?
- [Django]-What is a NoReverseMatch error, and how do I fix it?
-3👍
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.
- [Django]-How to make an auto-filled and auto-incrementing field in django admin
- [Django]-Django Server Error: port is already in use
- [Django]-What's the cleanest, simplest-to-get running datepicker in Django?