Contents

Django Manual Editing Auto DateTime Fields


TL;DR: use default=timezone.now, editable=False, blank=True to mimic the behavior of auto_now and auto_now_add.

Django’s auto_now and auto_now_add fields are built-in django model fields and used to track when a model was modified or created. They are suppose to be readonly fields. But there are times that I want to overwrite these two fields for some model instances. For example: I’d like to change my test data to match exact timestamp each time.

Models' auto_now and auto_now_add will set the field to have editable=False and blank=True.

1
2
3
4
5
from django.utils import timezone

class MyModel(models.Model):
    createdAt = models.DateTimeField(default=timezone.now, editable=False, blank=True)
    lastUpdatedAt = models.DateTimeField(default=timezone.now, editable=False, blank=True)
Note
By passing timezone.now without parentheses as the default value of DateTimeField, this function will be called each time a record is added. If, instead, timezone.now() was passed, the return value of timezone.now() will be used when the model is defined, not each time adding a record.

Temporarily disable auto_now and auto_now_add attributes

We can achieve this by changing model fields' attribute

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
model_instance = MyModel()

for field in model_instance._meta.get_fields():
    if field.name == 'createdAt':
        field.auto_now = False
    elif field.name == 'lastUpdatedAt':
        field.auto_now_add = False

model_instance.createAt = timezone.now()
model_instance.lastUpdatedAt = timezone.now()

for field in model_instance._meta.get_fields():
    if field.name == 'createdAt':
        field.auto_now = True
    elif field.name == 'lastUpdatedAt':
        field.auto_now_add = True

Use SQL update statement to bypass Django’s save() mechanism

Both auto_now and auto_now_add are relies on Django models' save() mechanism and thus we can use update() to bypass it.

1
MyModel.objects.filter(id=<instance_id>).update(createdAt=timezone.now())