Django auto_now and auto_now_add
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.
Difference between auto_now and auto_now_add
auto_now
takes precedence (obviously, because it updates field each time, while auto_now_add
updates on creation only). Here is the code for DateField.pre_save
method:
def pre_save(self, model_instance, add):
if self.auto_now or (self.auto_now_add and add):
value = datetime.date.today()
setattr(model_instance, self.attname, value)
return value
else:
return super().pre_save(model_instance, add)
As you can see, if auto_now
is set or both auto_now_add
is set and the object is new, the field will receive current day.
The same for DateTimeField.pre_save
:
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().pre_save(model_instance, add)
Django Models auto_now saving same time as auto_now_add
TL;DR: when you create an object for the first time, both created_at
and updated_at
will be filled with values, accordingly to the code you crafted.
- The
auto_now_add
will set thetimezone.now()
only when the instance is created. - The
auto_now
will update the field every time the save method is called.
However, both functions the same way by triggering the field update event with timezone.now()
.
This means that when you create an object for the first time, both created_at
and updated_at will
be filled with values.
But, the next time you save that object, the created_at will remain the same, while the updated_at will call the timezone.now()
.
The options auto_now_add, auto_now, and default are mutually exclusive. Any combination of these options will result in an error.
Django docs: https://docs.djangoproject.com/en/4.0/ref/models/fields/
Last Update field in Django... 'auto_now' or 'hidden' widget
auto_now Automatically set the field to now every time the object is saved.
So to save updated record time auto_now is the best option
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
https://docs.djangoproject.com/en/3.1/ref/models/fields/#datefield
Related Topics
Keyboard Interrupts with Python's Multiprocessing Pool
How to Replace Text in a String Column of a Pandas Dataframe
How to Specify the Function Type in My Type Hints
Beautifulsoup Webscraping Find_All( ): Finding Exact Match
Parse HTML Table to Python List
How to Use the Python HTMLparser Library to Extract Data from a Specific Div Tag
Is There a Built in Package to Parse HTML into Dom
Paramiko Error When Trying to Edit File: "Sudo: No Tty Present and No Askpass Program Specified"
Arranging Text Files Side by Side Using Python
Programming on Samsung Chromebook
Does Python Support Multithreading? Can It Speed Up Execution Time
Importerror: No Module Named Pil
Difference Between Subprocess.Popen and Os.System
Django Filter Queryset _In for *Every* Item in List
How to Split Elements of a List
Numpy Selecting Specific Column Index Per Row by Using a List of Indexes