Django auto_now and auto_now_add
Solution 1:
Any field with the auto_now
attribute set will also inherit editable=False
and therefore will not show up in the admin panel. There has been talk in the past about making the auto_now
and auto_now_add
arguments go away, and although they still exist, I feel you're better off just using a custom save()
method.
So, to make this work properly, I would recommend not using auto_now
or auto_now_add
and instead define your own save()
method to make sure that created
is only updated if id
is not set (such as when the item is first created), and have it update modified
every time the item is saved.
I have done the exact same thing with other projects I have written using Django, and so your save()
would look like this:
from django.utils import timezone
class User(models.Model):
created = models.DateTimeField(editable=False)
modified = models.DateTimeField()
def save(self, *args, **kwargs):
''' On save, update timestamps '''
if not self.id:
self.created = timezone.now()
self.modified = timezone.now()
return super(User, self).save(*args, **kwargs)
Hope this helps!
Edit in response to comments:
The reason why I just stick with overloading save()
vs. relying on these field arguments is two-fold:
- The aforementioned ups and downs with their reliability. These arguments are heavily reliant on the way each type of database that Django knows how to interact with treats a date/time stamp field, and seems to break and/or change between every release. (Which I believe is the impetus behind the call to have them removed altogether).
- The fact that they only work on DateField, DateTimeField, and TimeField, and by using this technique you are able to automatically populate any field type every time an item is saved.
- Use
django.utils.timezone.now()
vs.datetime.datetime.now()
, because it will return a TZ-aware or naivedatetime.datetime
object depending onsettings.USE_TZ
.
To address why the OP saw the error, I don't know exactly, but it looks like created
isn't even being populated at all, despite having auto_now_add=True
. To me it stands out as a bug, and underscores item #1 in my little list above: auto_now
and auto_now_add
are flaky at best.
Solution 2:
But I wanted to point out that the opinion expressed in the accepted answer is somewhat outdated. According to more recent discussions (django bugs #7634 and #12785), auto_now
and auto_now_add
are not going anywhere, and even if you go to the original discussion, you'll find strong arguments against the RY (as in DRY) in custom save methods.
A better solution has been offered (custom field types), but didn't gain enough momentum to make it into django. You can write your own in three lines (it's Jacob Kaplan-Moss' suggestion).
from django.db import models
from django.utils import timezone
class AutoDateTimeField(models.DateTimeField):
def pre_save(self, model_instance, add):
return timezone.now()
#usage
created_at = models.DateField(default=timezone.now)
updated_at = AutoDateTimeField(default=timezone.now)
Solution 3:
Talking about a side question: if you want to see this fields in admin (though, you won't be able to edit it), you can add readonly_fields
to your admin class.
class SomeAdmin(ModelAdmin):
readonly_fields = ("created","modified",)
Well, this applies only to latest Django versions (I believe, 1.3 and above)
Solution 4:
I think the easiest (and maybe most elegant) solution here is to leverage the fact that you can set default
to a callable. So, to get around admin's special handling of auto_now, you can just declare the field like so:
from django.utils import timezone
date_field = models.DateField(default=timezone.now)
It's important that you don't use timezone.now()
as the default value wouldn't update (i.e., default gets set only when the code is loaded). If you find yourself doing this a lot, you could create a custom field. However, this is pretty DRY already I think.