Django error when migrating with default field function that queries database

Viewed 1701

I'm trying to create a default field callable like this:

from datetime import date, timedelta
from django.db import models

class Preferences(models.Model):
    expiration_days = models.IntegerField(default=30)
    ... other db settings here ...

class ImportantBusinessObject(models.Model):
    expiration_date = models.DateField(default=get_default_expiration_date)

def get_default_expiration_date():
    expiration_days = Preferences.objects.first().expiration_days
    return date.today() + timedelta(days=expiration_days)

The idea is that there is a set of global preferences stored in the db, which affect default values on other model objects.

Everything is fine and dandy until it's time to add another field to the Preferences class. Then, when building the db (while running tests, for example), the migration which adds the expiration_date field ends up executing before the new migration adding another preference field. During the call to get_default_expiration_date(), the Preferences object is the latest version, but the DB hasn't gotten the newest migration, and I get a column preferences.new_preference does not exist error.

What's the best way to solve this?

Right now I'm disabling migrations during tests as a workaround, but I'm wondering if there's another way.

I've tried grabbing the model from the app registry from within get_default_expiration_date(), however it's still the latest one, not the versioned one that you're given in migrations.RunPython functions.

I've also tried wrapping the line that gets the Preferences object in a try/except block, but the db ends up in a rollback state, and I can't continue.

Other bits:

  • I'm using Django 1.8
  • In my actual app, the Preferences class is actually a db singleton using django-solo.

UPDATE: Clarifying the order of operations

Here is sort of a timeline of what's happening:

  1. preferences/migrations/initial_0001.py: initial preferences, includes expiration_days
  2. business/migrations/initial_0001.py: initial business object, depends on preferences 0001
  3. preferences/migrations/add_pref_0002.py: add a new field to Preferences

As soon as I define the migration in step #3, things start to break because when running to add the migration from step #2, get_default_expiration_date is called which throws the error. From the comment thread below about updating dependencies, my point was that in order to do that, every time I add a new preferences migration, I need to update the dependencies of the previously defined business/migrations/initial_0001.py (and any others that behave similarly). Hopefully this clarifies the situation a bit.

1 Answers
Related