theWanderer4865
theWanderer4865

Reputation: 871

Django Migrations fail during django initialization

The setup

A few months ago I upgraded an app from Django 1.6 to 1.7 then 1.8 and now I'm working on getting up to 1.9.

While wrangling with migrations I uncovered a pretty nasty instance of database state dependency - a management method on the (not so great) custom user model calls django.contrib.auth.models.Group. Yikes. So when I set up the Continuous Integration pipeline I managed to solve the problem with a data migration and everything was great.

The migration looks like this:

# import statements left out for brevity

def seed_groups(apps, schema_editor):
    Group = apps.get_model('auth', 'Group')
    Group.objects.get_or_create(name='group1')
    Group.objects.get_or_create(name='group2')
    Group.objects.get_or_create(name='group3')

class Migration(migrations.Migration):
    dependencies = [('auth', '0001_initial')]
    operations = [migrations.RunPython(seed_groups)]

Okay, so that's not totally great - the usage of get_or_create here allows us to hook up a database that already has data in it without making Postgres get super upset about asking it to insert rows it already has. This works though and we've been happy campers with tests running merrily and none of our environments have had any issues.

The twist

So I've been running my tests and fixing deprecations, updating libraries, blah blah blah. So it came as a surprise that my CI environment (a moderately popular service) is failing to build at the migration step, only since the Django version changed from 1.8 to 1.9.

I've checked that there isn't some sort of cached dependency chain issue (we're loading all the right libraries) but the traceback from the error is very familiar....

django.db.utils.ProgrammingError: relation "auth_group" does not exist
LINE 1: ...ELECT "auth_group"."id", "auth_group"."name" FROM "auth_grou...
^

Full traceback available here: https://gist.github.com/alexkahn/b63c41904809cbe53962dc104e4067f0

This error is cropping up from running python manage.py migrate --no-input

Things I've done in futile attempts to resolve the issue:

Modifying the seed_groups function like so:

def seed_groups(apps, schema_editor):
    # same
    db_alias = schema_editor.connection.alias
    Group.objects.using(db_alias).get_or_create(name='group1')
    # etc...

Adding an initial = True class attribute to my 0001 migration for this app.

Squash all of the migrations into one.

Pointing my installed apps list directly to this app's AppConfig subclass.

What I was thinking for some of those, I don't know.

Bottom Line

Anyone have a clue why this would suddenly change? Is there something super obvious that I am thinking too hard about?

Upvotes: 2

Views: 1058

Answers (2)

theWanderer4865
theWanderer4865

Reputation: 871

So this pulled out some new havoc from a...not so great way to do things with Django.

So we have a model manager here:

class UserManager(BaseUserManager):
    def users_in_group1(self):
        return Group.objects.get(name='group1').user_set.filter()

It's returning a queryset that directly interacts with the auth.models.Group model. Tight coupling between models here resulted in Django needing to resolve that relation before any tables were created.

A simple change to:

def users_in_group1(self):
    return self.filter(groups__name='group1')

Allows the migrations to run without issue.

Upvotes: 2

Alasdair
Alasdair

Reputation: 308779

The Django system checks run before the migrations. The URL system checks added in 1.9 check your URL config, which causes your views to be imported.

This part of the traceback shows that SuperUserAccountForm is causing queries when the module is loaded. This causes an error when you are migrating, because the table has not been created yet.

File "/home/rof/src/github.com/myapp/myapp/myapp/accounts/views.py", line 28, in <module>
from myapp.accounts.forms import (
File "/home/rof/src/github.com/myapp/myapp/myapp/accounts/forms.py", line 351, in <module>
class SuperUserAccountForm(forms.ModelForm):
File "/home/rof/src/github.com/myapp/myapp/myapp/accounts/forms.py", line 358, in SuperUserAccountForm
queryset=Account.objects.group1(),
File "/home/rof/src/github.com/myapp/myapp/myapp/accounts/models.py", line 71, in group1
return Group.objects.get(name='group1').user_set.filter()

Upvotes: 1

Related Questions