Handling New Objects Custom save() Methods

In Django, models represent tables in a database and instances of a model represent rows in the corresponding database table. When creating custom model methods like save(), it can be useful to identify if an instance is new or already existing in the database. This allows you to apply logic specific to new or existing objects. In this post, we’ll explore techniques for handling new objects in Django model custom save() methods.

The save() Method

The save() method is called when an object is saved to the database for the first time, and any subsequent times the object is saved after changes are made. The default implementation handles inserting or updating the model in database.

We can override the save() method on a model to add custom actions and business logic. For example:

from django.db import models

class Person(models.Model):
  name = models.CharField(max_length=100)

  def save(self, *args, **kwargs):
    # Custom logic here
    super().save(*args, **kwargs) 

Inside a custom save(), we may need to treat new objects differently than existing objects.

Identifying New Objects in custom save()

There are a couple ways we can identify new objects in a custom save() method:

1. Check if the Object has a Primary Key

When a model instance is created, it does not have a value for the primary key field until it is saved to the database for the first time. We can check if the primary key field has a value to determine if it is a new object:

from django.db import models

class Person(models.Model):
  id = models.AutoField(primary_key=True)
  name = models.CharField(max_length=100)

  def save(self, *args, **kwargs):
    if not self.id:
      # New object

    super().save(*args, **kwargs)

This works because Django model instances will not have an ID until the object is inserted into the database.

2. Check for existence in the database

We can also check if the object already exists in the database by querying for its existence. Django provides a handy id_exists property on model managers for this purpose:

from django.db import models

class Person(models.Model):
  name = models.CharField(max_length=100)

  def save(self, *args, **kwargs):
    if not self.__class__.objects.filter(id=self.id).exists():
        # New object

    super().save(*args, **kwargs)

This performs a query looking for any Person instance with the same primary key as the current object. If none exists, we know it is new.

Handling New Objects

Once we’ve identified new objects in save(), we can apply specific logic just for those objects.

For example:

from django.db import models

class Person(models.Model):
  name = models.CharField(max_length=100)

  def save(self, *args, **kwargs):
    if not self.pk: 
        # Set default values on new objects
        self.name = self.name or 'Anonymous'  

    super().save(*args, **kwargs)

Here we set a default name value only when the object is new.

Other common cases for handling new objects:

  • Setting default field values
  • Creating related objects
  • Performing one-time initialization

Existing objects would skip this logic when saved.

Conclusion

  • The save() method can be overridden on Django models to add custom logic
  • New objects can be identified by checking for a primary key value
  • Objects can also be queried to check for existence in the database
  • This allows applying logic specifically to the new objects
  • Examples include setting default values, creating related objects, and initialization

Using these techniques of checking the primary key or querying for existence allows us to robustly identify new objects in Django model custom save() methods. This opens up possibilities for useful logic when saving new model instances.