How to Lengthen the Username Field on Django User Model? - python

I have a Django web application that is deployed on Heroku. I'm using Django's user model for authentication. My goal is to lengthen the username field from 30 characters to 100 characters.
I overrode the default User model with my own AbstractUser model:
class AbstractUser(AbstractBaseUser, PermissionsMixin):
username = models.CharField(
_('username'),
max_length=30,
unique=True,
help_text=_('Required. 100 characters or fewer. Letters, digits and #/./+/-/_ only.'),
validators=[
validators.RegexValidator(
r'^[\w.#+-]+$',
_('Enter a valid username. This value may contain only '
'letters, numbers ' 'and #/./+/-/_ characters.')
),
],
error_messages={
'unique': _("A user with that username already exists."),
},
)
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
email = models.EmailField(_('email address'), blank=True)
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user can log into this admin site.'),
)
is_active = models.BooleanField(
_('active'),
default=True,
help_text=_(
'Designates whether this user should be treated as active. '
'Unselect this instead of deleting accounts.'
),
)
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
I want to update the max_length of the username field from 30 to 100. I can do this locally without any issue by simply updating the line max_length=30 to max_length=100, running python manage.py makemigrations and python manage.py migrate.
However, the migration file is created in my virtual environment (venv\Lib\site-packages\django\contrib\auth\migrations) folder so I'm not sure where to deploy it to Heroku. I have my entire venv folder excluded through my .gitignore file. I do not have a venv folder on Heroku.
Where/how should I deploy and apply the migration file to Heroku?

Related

Hi everybody. I'm making a shop with django and i have a problem with custom User

When I'm trying to delete my ShopUser i have this exception
I have not found a solution to this problem on the internet
Exception Type: OperationalError
Exception Value:
no such table: account_shopuser_groups
Also this exception resises when I want to migrate.
How can i solve this problem?
My CustomUserModel:
class ShopUser(AbstractUser):
username = models.CharField(
_('username'),
max_length=150,
unique=True,
help_text=_('Required. 150 characters or fewer. Letters, digits and #/./+/-/_ only.'),
error_messages={
'unique': _("A user with that username already exists."),
},
)
email = models.EmailField(_('email address'), unique=True)
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
is_superuser = models.BooleanField(
_('superuser status'),
default=False,
help_text=_(
'Designates that this user has all permissions without '
'explicitly assigning them.'
),
)
objects = CustomUserManager()
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
def has_perm(self, perm, obj=None):
return True
def __str__(self):
return self.username
my CustomUserAdmin
class ShopUserAdmin(UserAdmin):
add_form = ShopUserCreationForm
form = ShopUserChangeForm
model = ShopUser
list_display = ('email', 'is_staff', 'is_active',)
list_filter = ('email', 'is_staff', 'is_active',)
fieldsets = (
(None, {'fields': ('email', 'password',)}),
('Permissions', {'fields': ('is_staff', 'is_active')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active')}
),
)
search_fields = ('email',)
ordering = ('email',)
admin.site.register(ShopUser, ShopUserAdmin)
Thank you for help!
You need to migrate your models before using them.
Run makemigrations to create the migrations
python3 manage.py makemigrations account
Then run migrate to push them to your db:
python3 manage.py migrate
Edit:
You probably have an issue with your migration graph. If you don't care about the data in your database, the easiest way fix this is to delete your migrations, delete the database, then rerun your initial migrations. The following command will clear your migrations for you (unix-like OS only):
find . -path "*/migrations/*.py" -not -path "*/site-packages/*" -delete;
If you are on windows you will have to manually go to each of your application's migration folders, and delete every file.
Afterwards make sure you delete your db.sqlite3 file (or the schema for whatever database you are using), then run the migrations again.

Avoid username field creation in custom user model for django allauth

I'm using a custom user model with allauth and I need to have the username field omitted. I've already seen the docs and a whole bunch of stackoverflow answers about using ACCOUNT_USER_MODEL_USERNAME_FIELD = None but all of this still leads my database to have an username field.
Now since the db still has an username field with the unique constraint set on and allauth will not put a username in the field with the aforementioned setting set to None, this causes me to face IntegrityError after the very first user creation. I know I can solve this by just having the aforementioned setting be set to 'username' but I'm curious, how do I just not make the username, because I'm never using it.
My model:
class CustomUser(AbstractUser):
# Custom user model for django-allauth
first_name = None
last_name = None
def delete(self):
# Custom delete - make sure user storage is also purged
# Purge the user storage
purge_userstore(self.email)
# Call the original delete method to take care of everything else
super(CustomUser, self).delete()
It doesn't really do much except override the delete function. This override is irrelevant in this topic but I included it just for the sake of completeness. It also sets first_name and last_name to None, which works perfectly and removes those fields from the database as expected. I've tried setting user to None but that does nothing. I've also tried setting username to None but that will raise a FieldNotFound error with ACCOUNT_USER_MODEL_USERNAME_FIELD = None
My settings (the relevant bit):
AUTHENTICATION_BACKENDS = (
"django.contrib.auth.backends.ModelBackend",
"allauth.account.auth_backends.AuthenticationBackend",
)
AUTH_USER_MODEL = 'custom_account.CustomUser'
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_UNIQUE_EMAIL = True
ACCOUNT_AUTHENTICATION_METHOD = 'email'
My migration file:
migrations.CreateModel(
name='CustomUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and #/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],
....
This migration generation confuses me to no end. Why is the username field still there? Why is it set to be the only unique constraint even though I've clearly set ACCOUNT_UNIQUE_EMAIL = True?
Note: This migration file is generated from a clean slate, this is the first and only migration file generated from the code I have presented.
At first I thought, my settings were simply not being read. But I checked django.conf.settings and allauth.account.app_settings (in the shell) for these changes and they were all updated. What's going on here?
Note: Amongst the many stackoverflow questions I've searched, this question in particular seems to explain my issue perfectly. With one small problem, the answer by the creator of allauth himself suggested to use ACCOUNT_USER_MODEL_USERNAME_FIELD = "username" as the model in question was "clearly using the username field". But the answer does not explain what to do when you don't want to use the username field at all.
Looks like the only way to get rid of the username field is to override the AbstractUser's username field and/or use a completely custom model from the ground up. Thought overriding AbstractBaseUser should work too, albeit AbstractBaseUser provides less functionality.
class CustomUser(AbstractUser):
# Custom user model for django-allauth
# Remove unnecessary fields
username = None
first_name = None
last_name = None
# Set the email field to unique
email = models.EmailField(_('email address'), unique=True)
# Get rid of all references to the username field
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
This model will remove the username field and make the email field unique, as well as change all references to the USERNAME_FIELD to 'email'. Do note that REQUIRED_FIELDS should be empty as the USERNAME_FIELD cannot be in there. When using allauth, this is not a problem, the email and password requirement are managed by allauth anyway.
The settings that I've mentioned in the question should remain the same, specifically-
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_EMAIL_REQUIRED = True

Make Migrations on Django

I have a Django model which looks like this
class RedUsers(BaseModel):
user_email = models.EmailField(null=True, blank=True)
user_name = models.CharField(max_length=30, null=True, blank=True)
red_id = models.CharField(max_length=30, null=True, blank=True)
active = models.BooleanField(default=False)
def __str__(self):
return self.user_email
class Meta:
verbose_name_plural = "Red Users"
I want to add a new field
activation_key = models.CharField(max_length=40, null=True, blank=True)
I already have lots of data in this model and I can't drop the table, so I need to make migration manually.
I have tried adding the model from my 0001_initial.py file without luck
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name='RedUsers',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('modified', models.DateTimeField(auto_now=True)),
('user_email', models.EmailField(blank=True, max_length=254, null=True)),
('user_name', models.CharField(blank=True, max_length=30, null=True)),
('red_id', models.CharField(blank=True, max_length=30, null=True)),
('active', models.BooleanField(default=False)),
],
options={
'verbose_name_plural': 'RED Users',
},
),
migrations.AddField(
model_name='redusers',
name='activation_key',
field=models.CharField(blank=True, max_length=40, null=True, verbose_name='activation key'),
),
]
When I run
python manage.py migrate
It says Your models have changes that are not yet reflected in a migration, and so won't be applied.
I don't know what else to do
I want to add a new field
activation_key = models.CharField(max_length=40, null=True, blank=True)
I already have lots of data in this model and I can't drop the table,
so I need to make migration manually.
You don't need to. If you run makemigrations a second time, Django will detect that you added a field, and will make an extra migration file to add that field to the model.
You thus better here remove the AddField migration object from the migration file, and run python3 manage.py makemigrations.
It will make a new file, likely something that starts with 0002_….py, and it will take the first migration as dependency.
You can then run python3 manage.py migrate, and Django will add the field at the database level, and insert the name of the migration in the migration table.

Django Renaming/Refactoring a Custom User Model

I have a custom user model: class CustomUser(AbstractUser): already migrated and populated with some records. I want to rename this CustomUser to MyUserModel. In Pycharm I right-clicked on the name and chose refactor - rename so every reference to this model is also renamed.
I then ran python3 manage.py makemigrations which gave me the below error:
"AUTH_USER_MODEL refers to model '%s' that has not been installed" % settings.AUTH_USER_MODEL
So I went to myproject.settings and changed the AUTH_USER_MODEL to MyUserModel.
AUTH_USER_MODEL = 'users.MyUserModel'
The makemigrations then worked. and when I looked into the migration file it looked like this:
class Migration(migrations.Migration):
dependencies = [
('auth', '0011_update_proxy_permissions'),
('users', '0002_auto_20190916_0658'),
]
operations = [
migrations.CreateModel(
name='MyUserModel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and #/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
migrations.DeleteModel(
name='CustomUser',
),
]
I guess migration manager did not detect the rename but is creating the new table. Which I do not mind since I dont have important data yet.
when I then run python3 manage.py migrate the below error occurs:
ValueError: The field account.EmailAddress.user was declared with a lazy reference to 'users.myusermodel', but app 'users' doesn't provide model 'myusermodel'.
The field admin.LogEntry.user was declared with a lazy reference to 'users.myusermodel', but app 'users' doesn't provide model 'myusermodel'.
The field authtoken.Token.user was declared with a lazy reference to 'users.myusermodel', but app 'users' doesn't provide model 'myusermodel'.
The field socialaccount.SocialAccount.user was declared with a lazy reference to 'users.myusermodel', but app 'users' doesn't provide model 'myusermodel'.
Why is this happening? I also tried a migration with RenameModel rather than creating a new table and deleting the old one. Still got the same error.

Django best practice - referencing custom user or profile model

Based on Django's recommendation that information should be stored in a separate model if it's not directly related to authentication, I've created both a custom user model and a profile model in my app.
Something like:
class User(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True
)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
location = models.ForeignKey(Location)
date_of_birth = models.DateField()
date_joined = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name', 'location', 'date_of_birth']
class Profile(models.Model):
user = models.OneToOneField(User)
picture = models.ImageField(upload_to='profile_pictures/',
default='default.jpg')
bio = models.TextField(blank=True)
sex = models.CharField(max_length=10,
choices=(('Male', 'Male'),
('Female', 'Female'),
('No Comment', 'No Comment')),
default="No Comment")
occupation = models.CharField(max_length=100, blank=True)
What's the best practice for having other models refer to a user? For example, my app has a messaging system. In the Message model, is it best to have a foreignkey relation to Profile as opposed to User? Should the User model only be used for the purpose of authentication?
I think you can relate the models to User and use related name to access profile.
This makes your models not depend directly on custom profile.

Categories