475đź‘Ť
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)
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.
230đź‘Ť
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)
- [Django]-New url format in Django 1.9
- [Django]-Python (and Django) best import practices
- [Django]-How to go from django image field to PIL image and back?
46đź‘Ť
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)
- [Django]-Paginating the results of a Django forms POST request
- [Django]-What does error mean? : "Forbidden (Referer checking failed – no Referer.):"
- [Django]-Django: Fat models and skinny controllers?
39đź‘Ť
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.
- [Django]-How to get the current URL within a Django template?
- [Django]-Django 2, python 3.4 cannot decode urlsafe_base64_decode(uidb64)
- [Django]-Adding to the "constructor" of a django model
28đź‘Ť
class Feedback(models.Model):
feedback = models.CharField(max_length=100)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
Here, we have created and updated columns with a timestamp when created, and when someone modified feedback.
auto_now_add
will set the time when an instance is created whereas auto_now
will set the time when someone modified his feedback.
- [Django]-Why is logged_out.html not overriding in django registration?
- [Django]-Django rest framework lookup_field through OneToOneField
- [Django]-Django template can't see CSS files
19đź‘Ť
If you alter your model class like this:
class MyModel(models.Model):
time = models.DateTimeField(auto_now_add=True)
time.editable = True
Then this field will show up in my admin change page
- [Django]-Django related_name for field clashes
- [Django]-"<Message: title>" needs to have a value for field "id" before this many-to-many relationship can be used.
- [Django]-Github issues api 401, why? (django)
13đź‘Ť
Based on what I’ve read and my experience with Django so far, auto_now_add is buggy. I agree with jthanism — override the normal save method it’s clean and you know what’s hapenning. Now, to make it dry, create an abstract model called TimeStamped:
from django.utils import timezone
class TimeStamped(models.Model):
creation_date = models.DateTimeField(editable=False)
last_modified = models.DateTimeField(editable=False)
def save(self, *args, **kwargs):
if not self.creation_date:
self.creation_date = timezone.now()
self.last_modified = timezone.now()
return super(TimeStamped, self).save(*args, **kwargs)
class Meta:
abstract = True
And then, when you want a model that has this time-stampy behavior, just subclass:
MyNewTimeStampyModel(TimeStamped):
field1 = ...
If you want the fields to show up in admin, then just remove the editable=False
option
- [Django]-How to combine django "prefetch_related" and "values" methods?
- [Django]-How to assign items inside a Model object with Django?
- [Django]-Update all models at once in Django
6đź‘Ť
Is this cause for concern?
No, Django automatically adds it for you while saving the models, so, it is expected.
Side question: in my admin tool, those 2 fields aren’t showing up. Is that expected?
Since these fields are auto added, they are not shown.
To add to the above, as synack said, there has been a debate on the django mailing list to remove this, because, it is “not designed well” and is “a hack”
Writing a custom save() on each of my models is much more pain than using the auto_now
Obviously you don’t have to write it to every model. You can write it to one model and inherit others from it.
But, as auto_add
and auto_now_add
are there, I would use them rather than trying to write a method myself.
- [Django]-Django staticfiles not found on Heroku (with whitenoise)
- [Django]-How to tell if a task has already been queued in django-celery?
- [Django]-How to go from django image field to PIL image and back?
4đź‘Ť
As for your Admin display, see this answer.
Note: auto_now
and auto_now_add
are set to editable=False
by default, which is why this applies.
- [Django]-How to use pdb.set_trace() in a Django unittest?
- [Django]-Function decorators with parameters on a class based view in Django
- [Django]-What is the Simplest Possible Payment Gateway to Implement? (using Django)
3đź‘Ť
I needed something similar today at work. Default value to be timezone.now()
, but editable both in admin and class views inheriting from FormMixin
, so for created in my models.py
the following code fulfilled those requirements:
from __future__ import unicode_literals
import datetime
from django.db import models
from django.utils.functional import lazy
from django.utils.timezone import localtime, now
def get_timezone_aware_now_date():
return localtime(now()).date()
class TestDate(models.Model):
created = models.DateField(default=lazy(
get_timezone_aware_now_date, datetime.date)()
)
For DateTimeField
, I guess remove the .date()
from the function and change datetime.date
to datetime.datetime
or better timezone.datetime
. I haven’t tried it with DateTime
, only with Date
.
- [Django]-How can I upgrade specific packages using pip and a requirements file?
- [Django]-ModuleNotFoundError: No module named 'grp' on windows
- [Django]-Python 3 list(dictionary.keys()) raises error. What am I doing wrong?
1đź‘Ť
auto_now=True
didn’t work for me in Django 1.4.1, but the below code saved me. It’s for timezone aware datetime.
from django.utils.timezone import get_current_timezone
from datetime import datetime
class EntryVote(models.Model):
voted_on = models.DateTimeField(auto_now=True)
def save(self, *args, **kwargs):
self.voted_on = datetime.now().replace(tzinfo=get_current_timezone())
super(EntryVote, self).save(*args, **kwargs)
- [Django]-How to print BASE_DIR from settings.py from django app in terminal?
- [Django]-Django – SQL bulk get_or_create possible?
- [Django]-How do I deploy Django on AWS?
1đź‘Ť
You can use timezone.now()
for created and auto_now
for modified:
from django.utils import timezone
class User(models.Model):
created = models.DateTimeField(default=timezone.now())
modified = models.DateTimeField(auto_now=True)
If you are using a custom primary key instead of the default auto- increment int
, auto_now_add
will lead to a bug.
Here is the code of Django’s default DateTimeField.pre_save withauto_now
and auto_now_add
:
def pre_save(self, model_instance, add):
if self.auto_now or (self.auto_now_add and add):
value = timezone.now()
setattr(model_instance, self.attname, value)
return value
else:
return super(DateTimeField, self).pre_save(model_instance, add)
I am not sure what the parameter add
is. I hope it will some thing like:
add = True if getattr(model_instance, 'id') else False
The new record will not have attr
id
, sogetattr(model_instance, 'id')
will return False will lead to not setting any value in the field.
- [Django]-Django composite unique on multiple model fields
- [Django]-Adding css class to field on validation error in django
- [Django]-Custom django admin templates not working
0đź‘Ť
you can use this code technique for make a [creation date] (that automatically save date at creation of row and never update the date and time on every updation ) and a [updation date] (that change date and time every time when you update the row).
from django.db import models
from django.utils.timezone import now
class Artical(models.Model):
creation = models.DateTimeField(null=True, default=None, blank=True)
updation = models.DateTimeField(null=True,default=None,blank=True)
def save(self, *args, **kwargs):
if not self.creation:
self.creation = now()
self.updation = now()
super(Artical, self).save(*args, **kwargs)
- [Django]-Complete django DB reset
- [Django]-Login Page by using django forms
- [Django]-Django Admin app or roll my own?
0đź‘Ť
Some comments have been made about update_fields, but it has not been included in an answer, so I am placing an important revision here. By default when django saves an instance, it saves every field in that instance. This is wasteful when only one field is being updated, and a more economical method is to only save the field being changed. This is done in django by using the update_fields parameter as in:
qt = QueuedTask.objects.create()
qt.status = PENDING
qt.save(update_fields=['status'])
Now your save method should take this into consideration, otherwise in the above save, the last_modified field is not also saved (Note that I am not sure about created, but as you could create a new instance, and use save method with update fields, I also include the condition in the save):
def save(self, *args, **kwargs):
if self.id is None:
self.created_on = timezone.now()
update_fields = kwargs.get('update_fields')
if update_fields is not None and 'created_on' not in update_fields:
update_fields += ['created_on']
else:
self.last_modified = timezone.now()
update_fields = kwargs.get('update_fields')
if update_fields is not None and 'last_modified' not in update_fields:
update_fields += ['last_modified']
return super(QueuedTask, self).save(*args, **kwargs)
- [Django]-Django substr / substring in templates
- [Django]-Form with CheckboxSelectMultiple doesn't validate
- [Django]-How do I match the question mark character in a Django URL?