Watch your Django field defaults do not load objects if you use South
Written by
on
in
Snaking.
Just ran into a South migration issue. Basically we had a field that looked like this (simplified):
class MyModel( models.Model ):
hostname = models.CharField(
default = random_unique_hostname,
)
where random_unique_hostname() was written like so (again, simplified):
def random_unique_hostname():
test = generate_random()
try:
MyModel.objects.get( hostname = test )
except MyModel.DoesNotExist, err:
return test
else:
return random_unique_hostname()
the actual code is significantly more complex, but that illustrates the problem. When you try to generate a South migration to add some *other* field to this model (i.e. a few months after you've added this default), you'll see the migration creation fail because South is calling your default (ouch) with a database that doesn't have the new field (the pre-migration DB).
The solution is to use a minimal query, rather than calling .get, use .filter().count() or use .filter().values_list('id') or any other query that doesn't map to the whole object being loaded, just to check for unique-ness.
Comments
Comments are closed.
Pingbacks
Pingbacks are closed.
Etienne on 01/19/2012 8:49 p.m. #
For this kind of query you should use '.exists()' (need Django >= 1.2).
toad on 01/19/2012 9:27 p.m. #
i thought exists can only be used on querysets. a "get" would return the actual object no?
Peter Zsoldos on 01/20/2012 3:33 a.m. #
This is one of the big problems with south migrations - as a rule of thumb, one shouldn't use your models in them, exactly because South always uses the latest version of the models.
Mike Fletcher on 01/20/2012 8:05 a.m. #
.exists() does sound like the correct approach. These new-fangled things... sheesh, am I really now an old-and-crufty Django programmer using all the obsolete APIs (I started at 0.9.6)... so much for migrating all the projects to 1.3.