5

I have this code in my model:

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

After I migrate and restart uwsgi, I get first datetime in MariaDB now, and all next - exactly the same as first after resetting uwsgi.

2015-04-19 16:01:46
2015-04-19 16:01:46
2015-04-19 16:01:46
2015-04-19 16:01:46

I fixed it by changing code to:

added_time = models.DateTimeField(
    auto_now_add=True
)

Though I fixed the problem, I'm not really sure why there even was such behavior?

idchlife
  • 554
  • 1
  • 6
  • 16
  • I don't know much about Django, but my money is on `default` being evaluated only once, when the class is evaluated. It happens with keyword arguments to functions as well. Try: `def foo(argument=datetime.datetime.now()): print argument` and then test it with a `while` loop or something: `while True: foo()` You'll see that `argument` doesn't change – Savir Apr 19 '15 at 17:23
  • possible duplicate of [Django. default=datetime.now() problem](http://stackoverflow.com/questions/2771676/django-default-datetime-now-problem) – knbk Apr 19 '15 at 17:39
  • If you're here because `datetime.now` is not evaluating in your class defaults please see my post here: https://dhariri.com/posts/56f68c45d1befa569505cf0f – David Hariri Mar 26 '16 at 13:50

2 Answers2

27

default=datetime.datetime.now() is evaluated at parsing/compile time of the model. It is not changed afterwards. To evaluate now() at the time of adding/updating an object, you have to use:

default=datetime.datetime.now, which sets now as the callable. Django will call it at runtime.

Your solution of using auto_now_add is of course also correct (yet semantically different -- passing a default will set the value every time the model is saved, whereas auto_now_add only does it once, at creation time).

Don't dispair, this is avery common mistake.

miraculixx
  • 10,034
  • 2
  • 41
  • 60
  • thank you too, kind sir! you not only saved my day, but showed wonderful site with python gotchas – idchlife Apr 19 '15 at 18:26
  • In your last paragraph, you linked to a website explaining how the default arguments of a function are mutable and persistent. This is a completely different gotcha then the one that is in play in this question post: in Python, `example` gives the function, whereas `example()` calls the function and gives the return value, unlike, say, Perl, where `example` and `example()` are equivalent, and `&example` is the way to retrieve the reference to the function. – Flimm Mar 01 '17 at 15:49
  • yes, I agree these are actually different cases. However, it is the same root cause -- the argument is evaluated at compile time, not at runtime as one might assume on first sight. – miraculixx Mar 02 '17 at 17:07
7

You need to pass datetime.datetime.now instead of datetime.datetime.now() to default. Otherwise, the default value is computed when the model is initialized hence you always get the same value after a restart.

See the Django documentation for a more thorough explanation.

If using Django's time zones support, remember to use django.utils.timezone.now instead of datetime.datetime.now.

aumo
  • 5,344
  • 22
  • 25