Opened 10 years ago

Closed 10 years ago

Last modified 10 years ago

#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 Vidir Valberg Gudmundsson, 10 years ago

Component: UncategorizedMigrations

comment:2 by Vidir Valberg Gudmundsson, 10 years ago

Type: UncategorizedBug

comment:3 by Tim Graham, 10 years ago

Triage Stage: UnreviewedAccepted

One solution is to specify your own MIGRATION_MODULES for auth, e.g. {'auth': 'testapp.migrations'} (since you don't want the table for auth.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 if AUTH_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.

comment:4 by Andrew Godwin, 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 Vidir Valberg Gudmundsson, 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.

Last edited 10 years ago by Vidir Valberg Gudmundsson (previous) (diff)

in reply to:  4 comment:6 by anonymous, 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 Andrew Godwin, 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 Andrew Godwin <andrew@…>, 10 years ago

Resolution: fixed
Status: newclosed

In 8d2ac948a9583220d2324cf908bff3cc1d0beb2b:

Fixed #22853: Swapped models are now ignored for migration operations.

comment:9 by Andrew Godwin <andrew@…>, 10 years ago

In f355d253f81c78b595e25e84309b648c24208d73:

[1.7.x] Fixed #22853: Swapped models are now ignored for migration operations.

Note: See TracTickets for help on using tickets.
Back to Top