When building web applications with Django, it is common to have models that represent real-world entities like users, posts, comments etc. These models usually have fields like created_at
and updated_at
to track when the model instance was first created and last updated.
Setting these timestamp fields manually every time you create or update an instance can be tedious. Thankfully, Django provides some useful tools to automatically set creation and modification dates on your models. In this post we’ll explore a few different approaches.
Leveraging auto_now_add and auto_now
Django includes two model field options – auto_now_add
and auto_now
– that can automatically set creation and modification timestamps.
auto_now_add
will automatically set the field to the current datetime when the model instance is first created. For example:
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
Here, the created_at
field will be automatically set without any additional code when a new Post object is first saved to the database.
auto_now
works similarly, except it will update the field every time the model instance is saved, not just on creation. This is useful for tracking last modification timestamps:
updated_at = models.DateTimeField(auto_now=True)
The `auto_now*_` fields provide a simple way to track the creation and update times without much effort.
Overriding model save() method
For example:
from django.utils import timezone
class Post(models.Model):
# fields…
def save(self, *args, **kwargs):
if not self.id:
self.created_at = timezone.now()
self.updated_at = timezone.now()
return super().save(*args, **kwargs)
Here we check if the modelinstance already has an id set – if not, we know this is the first time it is being saved so we set `created_at`. Then we always set the `updated_at` timestamp before calling the parent `save()` method.
For example:
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils import timezone
@receiver(pre_save)
def set_timestamps(sender, instance, **kwargs):
if not instance.id:
instance.created_at = timezone.now()
instance.updated_at = timezone.now()
With signals, we keep the timestamp logic in one place instead of each model. And signals work for any modelclasses, without changes to their code.
Using Model Forms
Thankfully, ModelForms have a hook that allows setting initial values during instantiation:
from django import forms
class PostModelForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Set created_at for new instances
if not self.instance.pk:
self.initial['created_at'] = timezone.now()
# Set updated_at when editing existing instances
self.initial['updated_at'] = timezone.now()
Here we check if the form has an associated model instance – if not, we know it is for creating a new instance so we set created_at
. And we always set updated_at
in case the form is editing an existing instance.
The initial
values will be picked up when calling form.save()
to create or update the model.
This keeps the timestamp logic in the ModelForm class itself, avoiding the need to modify the model. The downside is that it only works when creating/updating instances via the ModelForm, not directly through the modelclass.
Summary
Automatically setting creation and update timestamps on Django models is handy for tracking changes over time. Some options include:
- Using the built-in
auto_now
andauto_now_add
model field options - Overriding the model
save()
method - Attaching signal receivers that set timestamps
- Setting initial values in ModelForms
The right approach depends on your specific models, forms, and use cases. Mixing and matching techniques can be useful for Django models with varying requirements.