Django Renaming/Refactoring a Custom User Model - python

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.

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.

How to Lengthen the Username Field on Django User Model?

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?

one of my few django migrations does not run [duplicate]

This question already has answers here:
Rerun a Django data migration
(3 answers)
Closed 5 years ago.
I have a django migration:
0020_delete_cesiumentity
which deletes a table...
then I rebuild it (this is was trying to fix a previous problem I had) with this migration I created:
0021_cesiumentity.py
# -*- coding: utf-8 -*-
# Generated by Django 1.9.5 on 2016-12-19 22:45
from __future__ import unicode_literals
import django.contrib.gis.db.models.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('swsite', '0020_delete_cesiumentity'),
]
operations = [
migrations.CreateModel(
name='CesiumEntity',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('be_number', models.CharField(max_length=100)),
('image_id', models.CharField(blank=True, max_length=100, null=True)),
('mission_id', models.CharField(blank=True, max_length=100, null=True)),
('product_type', models.CharField(blank=True, max_length=100, null=True)),
('polarization', models.CharField(blank=True, max_length=256, null=True)),
('mode_id', models.CharField(blank=True, max_length=100, null=True)),
('mode_name', models.CharField(blank=True, max_length=100, null=True)),
('acquisition_type', models.CharField(blank=True, max_length=100, null=True)),
('image_size_samples', models.CharField(blank=True, max_length=100, null=True)),
('image_size_lines', models.CharField(blank=True, max_length=100, null=True)),
('sample_spacing_range', models.CharField(blank=True, max_length=100, null=True)),
('line_spacing_azimuth', models.CharField(blank=True, max_length=100, null=True)),
('pass_direction', models.CharField(blank=True, max_length=100, null=True)),
('look_direction', models.CharField(blank=True, max_length=100, null=True)),
('grazing_angle', models.CharField(blank=True, max_length=100, null=True)),
('azimuth_angle', models.CharField(blank=True, max_length=100, null=True)),
('doppler_cone_angle', models.CharField(blank=True, max_length=100, null=True)),
('file_format', models.CharField(blank=True, max_length=100, null=True)),
('name', models.CharField(max_length=100)),
('file_name', models.CharField(max_length=256)),
('country_code', models.CharField(blank=True, max_length=64, null=True)),
('collection_date', models.DateField(blank=True, null=True)),
('collection_start_time', models.CharField(blank=True, max_length=256, null=True)),
('corner_coords', models.CharField(max_length=255)),
('sensor', models.CharField(max_length=128)),
('target_name', models.CharField(blank=True, max_length=256, null=True)),
('file_size', models.IntegerField()),
('dzi_location', models.CharField(max_length=256)),
('kml_location', models.CharField(max_length=256)),
('kmz_location', models.CharField(max_length=256)),
('thumbnail_location', models.CharField(max_length=256)),
('resource_location', models.CharField(max_length=256)),
('processed', models.BooleanField(default=False)),
('created_at', models.DateField(auto_now_add=True)),
('updated_at', models.DateField(auto_now=True)),
('mpoly', django.contrib.gis.db.models.fields.PolygonField(srid=4326)),
],
),
]
This migration does not run, causing my 0022 migration to fail since the table is not put back. I am not sure why this does not run or if I need to do something more. I tried to force it with a:
python manage.py migrate swsite 0021_cesiumentity
and I just get this:
Operations to perform:
Target specific migration: 0021_cesiumentity, from swsite
Running migrations:
No migrations to apply.
So I am not sure why this is happening or what I am missing?
I had issues with previous migrations saying things existed (which makes sense I am not working on my development but my test server), so I just faked those if that matters I am not sure.
Perhaps you made a mistake when faking migrations.
What does python manage.py showmigrations output? If it shows 0021_cesiumentity as being applied, then running python manage.py migrate swsite 0021_cesiumentity will have no effect.
To re-run that migration, you would have to fake back to the migration before (0020), then rerun python manage.py migrate swsite 0021_cesiumentity.
Django migrations are tracked using the 'django_migrations' table in your database. This is how Django records what migrations have been applied and what hasn't. If you check in this table, it will show you the migration file names that have been run on the DB.
If you want to rerun a migration, you need to delete the row in the django_migrations table with the corresponding file name. Then migrate again.

Categories