A Default Bug in Django

Not to say that this is a bug in django but rather a bug in how I had written some of my models.

Just wanted to point out a quick tip for anyone else who may have done the same thing as me as well as for any of the django experts out there that might suggest a better way.

What was happening was that I began to notice a wild discreptancy between two datetime columns in several models. They should have been identical, however, they were both getting their dates differently:

...
date_column_one = models.DateTimeField(default=datetime.now())
date_column_two = models.DateTimeField(auto_now_add=True)
...

What seems to be happening with this configuration is that datetime.now() is executed once when the server starts (and/or the module is imported) instead of what I thought would happen when I initially wrote these and that datetime.now() would get executed anytime an instance of the model was created and saved as a new record.

My solution was to override the save method:

...
date_column_one = models.DateTimeField()
...
def save(self):
    if not self.id:
        self.date_column_one = datetime.now()
    super(MyModel, self).save()
...

I am sure there is probably a better way and if you know of one, please leave a comment or send me an email and let me know!

19 comments ↓

#1 Ben on 05.07.08 at 5:34 pm

Try models.DateTimeField(default=datetime.now)

Django will execute the function in the way I think you expect.

#2 Sam on 05.07.08 at 5:35 pm

You can do it like so: models.DateTimeField(default=datetime.now)

#3 Meir Kriheli on 05.07.08 at 5:41 pm

You should use the callable:

datecolumnone = models.DateTimeField(default=datetime.now)

#4 Tom on 05.07.08 at 5:47 pm

datecolumnone = models.DateTimeField(default=datetime.datetime.now)

Pass the function, not call it.

#5 semente on 05.07.08 at 5:48 pm

Hi, uses models.DateTimeField(default=datetime.now)… without parenthesis…

#6 Jonathan Buchanan on 05.07.08 at 5:52 pm

You need to pass “default” a callable if you want it to be called every time a new object is being created:

http://www.djangoproject.com/documentation/model-api/#default

In the sample code above, you’re passing it a datetime.datetime object, as you’re actually calling the datetime.now() method instead of just passing a reference to it:

datecolumnone = models.DateTimeField(default=datetime.now)

#7 Dave Jeffery on 05.07.08 at 6:03 pm

Just use the callable instead (no ending parenthesis). e.g…

datecolumnone = models.DateTimeField(default=datetime.now)

It’s a common mistake particularly because you’re not likely to notice it when running the dev server.

Try not to use autonowadd, it’s deprecated or will be soon.

#8 Justin on 05.07.08 at 6:05 pm

I’ve been tripped up by similar behavior. What you probably want is to pass the callable to the default argument rather than the result of the callable; see:

http://www.djangoproject.com/documentation/models/field_defaults/

#9 Eric Florenzano on 05.07.08 at 6:08 pm

The easiest way to do it is like so: datecolumnone = models.DateTimeField(default=datetime.now)

Reason being, you can pass a callable for the default value that will be evaluated upon save. In this case, the callable results in the current date and you get what you want.

#10 Marco on 05.07.08 at 6:13 pm

Maybe you are looking for models.DateTimeField(auto_now = True) ?

http://www.djangoproject.com/documentation/model-api/#datetimefield

#11 Alex on 05.07.08 at 6:15 pm

Default can be given a callable, so you can do, default=datetime.now

and it will be called at save time.

#12 Andy Robinson on 05.07.08 at 6:18 pm

The ‘default’ argument to any field can be a callable. If you use “default=datetime.now” (note the lack of () at the end), it will call it as a function to get the time when a record is created, rather than evaluating it when the module is imported as you are doing now. (This is in the model_api docs page)

#13 Scott Robertson on 05.07.08 at 6:25 pm

Try passing “default=datetime.now” leaving off the parens will pass the function, and it’ll be called everytime.

#14 Dave Jeffery on 05.07.08 at 6:40 pm

LOL, 13 comments (including mine) saying the same thing. Boo for comment moderation!

#15 Kyle on 05.07.08 at 7:46 pm

Oh! Easy! Simply pass a callable rather than an actual value!

default=datetime.now

#16 Patrick Altman on 05.07.08 at 8:36 pm

Sounds good everyone! Thanks for the quick feedback.

#17 sean on 05.07.08 at 11:19 pm

hehe, this solves my problem too! thanks

#18 Garth Roxburgh-Kidd on 05.08.08 at 7:08 pm

This is one of two classic blunders with default arguments in Python. I’ve written them up because I’m so glad to find out you can pass callables to default= in Django fields. I’d never have known if I hadn’t stumbled across your tweet. Thanks!

http://www.deadlybloodyserious.com/2008/05/default-argument-blunders/

#19 lihs on 05.09.08 at 4:25 pm

You can do it datecolumnone = models.DateTimeField(autonowadd = true)

Leave a Comment