Django auto_now Removal Gotcha

Posted on | May 6, 2007 | 2 Comments

DjangoThe latest Django revisions have removed the shortcut handlers "auto_now&quot and "auto_now_add". This didn’t look like a very big deal to replace. I simply did what was suggested in a django-users thread.

The problem was that after I did that, my live code seemed to work fine, but my test cases had several repeatable errors that I just couldn’t reproduce on the live server or at the command line.

The Fix

The fix involves overriding the "save" method, like so (quoting the thread)


class MyModel(models.Model):
    self.created = models.DatetimeField()
    self.modified = models.DatetimeField()

    def save(self):
        if not self.id:
            self.created = datetime.datetime.now()
        else:
            self.modified = datetime.datetime.now()

        super(MyModel, self).save() 

What went wrong

After a lot of debugging and shrewd guessing, I finally had the eureka moment. The problem is that some of the test code was directly instantiating objects via calls like MyModel.objects.get_or_create(name="this", field="that"). In some places the test cases were using the even simpler MyModel.objects.create(name="this", field="that").

It finally occurred to me while walking the dog this morning that creating objects this way will skip the overridden "save()" method. I rushed to the computer to test my theory, and found that if I replaced:


MyModel.objects.get_or_create(name="this", field="that")
with this:

try:
    m = MyModel.objects.get(name="this", field="that")
except MyModel.DoesNotExist:
    m = MyModel(name="this", field="that")
    m.save()
everything worked perfectly.

So, watch out for that gotcha now that you can’t use auto_now in models any more.

Comments

2 Responses to “Django auto_now Removal Gotcha”

  1. Malcolm Tredinnick
    May 7th, 2007 @ 5:53 pm

    There’s something else going on here, I suspect. Both the create() and get_or_create() method do call your model’s save() method. In fact, get_or_create() is implemented more or less exactly as in your second example.

    If you want to trace throught the code, MyModel.objects.get_or_create() calls django.db.models.manager.Manager.get_or_create() which just passes off the real work to django.db.models.query.QuerySet.get_or_create() and you can see that calling obj.save() when required.

    So it would be interesting to understand what is really changing when you make the substitution you suggest above.

  2. Ramin
    February 24th, 2008 @ 10:03 am

    Minor typo.

    models.DatetimeField() should be models.DateTimeField() (with capitalized ‘T’).

Leave a Reply





CommentLuv Enabled

Video & Audio Comments are proudly powered by Riffly