#22853 closed Bug (fixed)
auth.0001_initial migration fails when there is a custom user model
Reported by: | Vidir Valberg Gudmundsson | Owned by: | nobody |
---|---|---|---|
Component: | Migrations | Version: | dev |
Severity: | Release blocker | Keywords: | |
Cc: | Triage Stage: | Accepted | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Models
from django.db import models from django.contrib.auth.models import AbstractUser class Profile(AbstractUser): title = models.CharField(max_length=255)
Migration
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations import django.core.validators import django.utils.timezone class Migration(migrations.Migration): dependencies = [ ('auth', '__first__'), ] operations = [ migrations.CreateModel( name='Profile', fields=[ ('id', models.AutoField(serialize=False, verbose_name='ID', auto_created=True, primary_key=True)), ('password', models.CharField(max_length=128, verbose_name='password')), ('last_login', models.DateTimeField(default=django.utils.timezone.now, 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(help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=30, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username.', 'invalid')], verbose_name='username')), ('first_name', models.CharField(max_length=30, blank=True, verbose_name='first name')), ('last_name', models.CharField(max_length=30, blank=True, verbose_name='last name')), ('email', models.EmailField(max_length=75, blank=True, 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')), ('title', models.CharField(max_length=255)), ('groups', models.ManyToManyField(blank=True, to='auth.Group', verbose_name='groups')), ('user_permissions', models.ManyToManyField(blank=True, to='auth.Permission', verbose_name='user permissions')), ], options={ 'abstract': False, 'verbose_name_plural': 'users', 'verbose_name': 'user', }, bases=(models.Model,), ), ]
Error
$ ./manage.py migrate Operations to perform: Synchronize unmigrated apps: (none) Apply all migrations: testapp, sessions, contenttypes, auth, admin Synchronizing apps without migrations: Creating tables... Installing custom SQL... Installing indexes... Running migrations: Applying auth.0001_initial...Traceback (most recent call last): File "./manage.py", line 10, in <module> execute_from_command_line(sys.argv) File "/Users/valberg/Code/forks/django/django/core/management/__init__.py", line 385, in execute_from_command_line utility.execute() File "/Users/valberg/Code/forks/django/django/core/management/__init__.py", line 377, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File "/Users/valberg/Code/forks/django/django/core/management/base.py", line 288, in run_from_argv self.execute(*args, **options.__dict__) File "/Users/valberg/Code/forks/django/django/core/management/base.py", line 337, in execute output = self.handle(*args, **options) File "/Users/valberg/Code/forks/django/django/core/management/commands/migrate.py", line 146, in handle executor.migrate(targets, plan, fake=options.get("fake", False)) File "/Users/valberg/Code/forks/django/django/db/migrations/executor.py", line 62, in migrate self.apply_migration(migration, fake=fake) File "/Users/valberg/Code/forks/django/django/db/migrations/executor.py", line 96, in apply_migration migration.apply(project_state, schema_editor) File "/Users/valberg/Code/forks/django/django/db/migrations/migration.py", line 107, in apply operation.database_forwards(self.app_label, schema_editor, project_state, new_state) File "/Users/valberg/Code/forks/django/django/db/migrations/operations/models.py", line 36, in database_forwards schema_editor.create_model(model) File "/Users/valberg/Code/forks/django/django/db/backends/schema.py", line 282, in create_model if field.rel.through._meta.auto_created: AttributeError: 'NoneType' object has no attribute '_meta'
Occurrences
This happens at 57a770b8e586399926311d86639c6789a1563253 in stable/1.7.x
and 61d7ae31cf286277ddefaf8006597be40388d2ee in master
.
Change History (9)
comment:1 by , 10 years ago
Component: | Uncategorized → Migrations |
---|
comment:2 by , 10 years ago
Type: | Uncategorized → Bug |
---|
comment:3 by , 10 years ago
Triage Stage: | Unreviewed → Accepted |
---|
follow-up: 6 comment:4 by , 10 years ago
The problem is that we can't just ignore the model when it's swapped out, as you can swap it back in _at any time_ and Django is supposed to work.
We have three options here:
- Just don't apply any operations on swapped models, like if they were denied routing or were managed=False. If you unswap them, you have to manually undo and reapply the auth migrations.
- Change it so that M2Ms are fully populated on swapped-out models and make the tables anyway so you can swap them back in at any time (but this clutters up the database with needless migrations)
- Make people do this custom migration modules thing.
My opinion is that we should ignore operations on swapped models (the first one), and make it very, very clear that you should not change AUTH_USER_MODEL
after starting the project without expecting some serious pain or manual kerfuffling with migrate --fake
. Thoughts?
comment:5 by , 10 years ago
Of the three options I'm also leaning towards the first one, mainly because doing this custom migration thing is confusing, and having a cluttered database is no fun!
But what about the option of having a migration that will be used if and only if AUTH_USER_MODEL != 'auth.User'? This should of course be generalized so it is doable with everything swappable.
comment:6 by , 10 years ago
If you want to lock AUTH_USER_MODEL and not change it in future, Whats the idea of migration then? Does not make sense to me do it halfway like this.
comment:7 by , 10 years ago
I mean, we already have the case where you can't really change AUTH_USER_MODEL midway, as any FKs that have already been created point to the old one. We're just restating the current state of things (and probably putting a much more obvious callout in the docs).
I'll go implement the first idea now.
Anonymous: The idea of migrations is to allow you to evolve the project as it changes. Changing AUTH_USER_MODEL essentially rewrites a whole load of foreign keys to point to a different table with different data that probably isn't even there; changing mid-project already means managing the move of data from auth.User to the new table. The option is there so you can start with your own user model and still use Django's admin and other tools.
comment:8 by , 10 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
One solution is to specify your own
MIGRATION_MODULES
forauth
, e.g.{'auth': 'testapp.migrations'}
(since you don't want the table forauth.models.User
anyway). I wonder if we should ship that migration (without the user model) with Django. Then we might be able to use it automatically ifAUTH_USER_MODEL
is in use.Actually, should the fact the the model has been swapped out cause it to be ignored?
I think we can consolidate #22854 ("Having custom user model creates migrations in django.contrib.auth") with this ticket as well.