I have a Django model that looks something like this:


class MyModel(models.Model):
   modify_date = models.DateTimeField(auto_now=True)
   ...

Retroactively now I wanted to add a field called add_date which uses the auto_now_add=True trick. The migration used in this project is South which is great but doesn't work very well with the auto_now_add=True because the field doesn't have a straight forward default. So, first I changed the field to this:


class MyModel(models.Model):
   modify_date = models.DateTimeField(auto_now=True)
   add_date = models.DateTimeField(auto_now_add=True, null=True)
   ...

Notice the null=True which is important. Then I used startmigration to generate the code for the forward and backward to which I added a this stuff:


class Migration:

   def forwards(self, orm):

       db.add_column('myapp_mymodel', 'add_date', orm['myapp.mymodel:add_date'])
       for each in MyModel.objects.all():
           # since MyModel is referenced elsewhere I can work out the oldest date
           oldest_date = get_oldest_related_date(each, 
                              default=each.modify_date)
           each.add_date = oldest_date
           each.save()

That way all old records will have the date (not entirely accurate but good enough) and all new records will automatically get a date. Is there a better way? I bet, but I don't know how to do it.

Comments

Carl

I think the better way is to avoid using auto_now_add in the first place, and instead use the more explicit "default=datetime.datetime.now" (or utcnow). Then South can handle it without assistance.

Nirvisual

Carl: Using "default=datetime.datetime.now" doesn't work with South migrations. It causes South to evaluate the expression, hard-coding today's date in the migration. Then every time you run the migration you get that date, regardless of the actual date.

Carl

Hmm, apparently South 0.6 doesn't actually handle dynamic defaults well at all; I was basing this comment off the last time I did it, with 0.5, when it worked fine. So never mind ;-)

Though apparently the next version of South will handle it well again (by ignoring defaults entirely): http://south.aeracode.org/ticket/273

Gord

We use something like the following when migration data containing fields that have auto_now or auto_now_add:

#Hack must override the pre_save to get touched to update correctly.
def no_touch_pre_save(self, model_instance, add):
return super(models.fields.DateField, self).pre_save(model_instance, add)
#End override touched.

def no_auto_add(fn):
"""
Hack django to not update touched - used for migrations.
"""
def _no_auto_add(*args, **kwargs):
#Force users to be explicit in this method about touched fields.
old_pre_save = models.fields.DateField.pre_save
models.fields.DateField.pre_save = no_touch_pre_save

#Call fn
ret = fn(*args, **kwargs)

#Revert to old skool pre_save in case.
models.fields.DateField.pre_save = old_pre_save
return ret
return _no_auto_add

Fabian Topfstedt

In your case I would monkeypatch the fields of MyModel while migrating:

from django.db.models.fields import DateTimeField
from my_app.models import MyModel

def forwards(self, orm):
for f in [f for f in MyModel._meta.fields if isinstance(f, DateTimeField)]:
f.auto_now_add = False
f.auto_now = False
# Your data migration code here...

Your email will never ever be published.

Previous:
Homer Simpson playing Cry of the Black Birds by Amon Amarth December 15, 2009 Music
Next:
China wrecked the Copenhagen deal December 23, 2009 Politics
Related by category:
How to avoid a count query in Django if you can February 14, 2024 Django
How to have default/initial values in a Django form that is bound and rendered January 10, 2020 Django
My site's now NextJS - And I (almost) regret it already December 17, 2021 Django
How to sort case insensitively with empty strings last in Django April 3, 2022 Django
Related by keyword:
How much faster is Redis at storing a blob of JSON compared to PostgreSQL? September 28, 2019 Python, PostgreSQL, Redis
UPPER vs. ILIKE April 19, 2010 Web development
Django ORM optimization story on selecting the least possible February 22, 2019 Python, Web development, Django, PostgreSQL
Django forms and making datetime inputs localized December 4, 2015 Python, Django