Automatic Setting Creation Dates for Django Model

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 and auto_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.