Often people make use of datetime.now
as
a default value to specify a field that stores when a
record was created, so something like:
from datetime import datetime
from django.db import models
class Post(models.Model):
created_on = models.DateTimeField(default=datetime.now)
Why is it a problem?
A field is by default editable and not optional. This
thus means that if you construct a
ModelForm
with
fields = '__all__'
, then this will include
the created_on
in the form. Normally we do
not want to include this. While it is of course possible
to create a DateTimeField
with
blank=True
and editable=False
,
but if later additional features arise, one needs to
specify more attributes.
Furthermore datetime.now()
[python-doc]
does not include a timezone. This thus means that the
database will store the timestamp without timezone. If
thus later the server works with a different timezone,
it will render different timestamps.
If one makes use of the freezegun
[GitHub], then making use of
datetime.now
directly will not work.
Indeed, if we first define a reference to
now()
, then freezing the time will not have
impact:
>>> from datetime import datetime
>>> from freezegun import freeze_time
>>> nw = datetime.now
>>> with freeze_time('1958-3-25'):
... print(nw())
... print(datetime.now())
...
2020-12-06 17:05:35.861048
1958-03-25 00:00:00
so tests with the freezegun will not work.
What can be done to resolve the problem?
Django's DateTimeField
and
DateField
has a auto_now_add=…
parameter [Django-doc]. By setting this
parameter to True
, you automatically use
the current timestamp when you construct the model
object. We thus can construct a
DateTimeField
with
auto_now_add=True
:
from django.db import models
class Post(models.Model):
created_on = models.DateTimeField(auto_now_add=True)
Extra tips
Django's DateTimeField
and
DateField
have a auto_now=…
parameter [Django-doc] as well. This is a
field that will each time take the current timestamp
when you update the record, so this can be used for an
updated_at
field.