In a django model custom save() method, how should you identify a new object?

DjangoDjango Models

Django Problem Overview


I want to trigger a special action in the save() method of a Django model object when I'm saving a new record (not updating an existing record.)

Is the check for (self.id != None) necessary and sufficient to guarantee the self record is new and not being updated? Any special cases this might overlook?

Django Solutions


Solution 1 - Django

Alternative way to checking self.pk we can check self._state of the model

self._state.adding is True creating

self._state.adding is False updating

I got it from this page

Solution 2 - Django

Updated: With the clarification that self._state is not a private instance variable, but named that way to avoid conflicts, checking self._state.adding is now the preferable way to check.


self.pk is None:

returns True within a new Model object, unless the object has a UUIDField as its primary_key.

The corner case you might have to worry about is whether there are uniqueness constraints on fields other than the id (e.g., secondary unique indexes on other fields). In that case, you could still have a new record in hand, but be unable to save it.

Solution 3 - Django

Checking self.id assumes that id is the primary key for the model. A more generic way would be to use the pk shortcut.

is_new = self.pk is None

Solution 4 - Django

The check for self.pk == None is not sufficient to determine if the object is going to be inserted or updated in the database.

The Django O/RM features an especially nasty hack which is basically to check if there is something at the PK position and if so do an UPDATE, otherwise do an INSERT (this gets optimised to an INSERT if the PK is None).

The reason why it has to do this is because you are allowed to set the PK when an object is created. Although not common where you have a sequence column for the primary key, this doesn't hold for other types of primary key field.

If you really want to know you have to do what the O/RM does and look in the database.

Of course you have a specific case in your code and for that it is quite likely that self.pk == None tells you all you need to know, but it is not a general solution.

Solution 5 - Django

You could just connect to post_save signal which sends a "created" kwargs, if true, your object has been inserted.

http://docs.djangoproject.com/en/stable/ref/signals/#post-save

Solution 6 - Django

Check for self.id and the force_insert flag.

if not self.pk or kwargs.get('force_insert', False):
    self.created = True

# call save method.
super(self.__class__, self).save(*args, **kwargs)

#Do all your post save actions in the if block.
if getattr(self, 'created', False):
    # So something
    # Do something else

This is handy because your newly created object(self) has it pk value

Solution 7 - Django

I'm very late to this conversation, but I ran into a problem with the self.pk being populated when it has a default value associated with it.

The way I got around this is adding a date_created field to the model

date_created = models.DateTimeField(auto_now_add=True)

From here you can go

created = self.date_created is None

Solution 8 - Django

For a solution that also works even when you have a UUIDField as a primary key (which as others have noted isn't None if you just override save), you can plug into Django's post_save signal. Add this to your models.py:

from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=MyModel)
def mymodel_saved(sender, instance, created, **kwargs):
    if created:
        # do extra work on your instance, e.g.
        # instance.generate_avatar()
        # instance.send_email_notification()
        pass

This callback will block the save method, so you can do things like trigger notifications or update the model further before your response is sent back over the wire, whether you're using forms or the Django REST framework for AJAX calls. Of course, use responsibly and offload heavy tasks to a job queue instead of keeping your users waiting :)

Solution 9 - Django

rather use pk instead of id:

if not self.pk:
  do_something()

Solution 10 - Django

It is the common way to do so.

the id will be given while saved first time to the db

Solution 11 - Django

> def save_model(self, request, obj, form, change):
>         if form.instance._state.adding:
>             form.instance.author = request.user
>             super().save_model(request, obj, form, change)
>         else:
>             obj.updated_by = request.user.username
> 
>             super().save_model(request, obj, form, change)

Solution 12 - Django

Would this work for all the above scenarios?

if self.pk is not None and <ModelName>.objects.filter(pk=self.pk).exists():
...

Solution 13 - Django

To know whether you are updating or inserting the object (data), use self.instance.fieldname in your form. Define a clean function in your form and check whether the current value entry is same as the previous, if not then you are updating it.

self.instance and self.instance.fieldname compare with the new value

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMikeNView Question on Stackoverflow
Solution 1 - DjangoSaintTailView Answer on Stackoverflow
Solution 2 - DjangoDave W. SmithView Answer on Stackoverflow
Solution 3 - DjangoGerryView Answer on Stackoverflow
Solution 4 - DjangoKayEssView Answer on Stackoverflow
Solution 5 - DjangoJF SimonView Answer on Stackoverflow
Solution 6 - DjangoKwaw AnnorView Answer on Stackoverflow
Solution 7 - DjangoJordanView Answer on Stackoverflow
Solution 8 - DjangometakermitView Answer on Stackoverflow
Solution 9 - DjangoyedpodtrzitkoView Answer on Stackoverflow
Solution 10 - DjangovikingosegundoView Answer on Stackoverflow
Solution 11 - DjangoSwelan AugusteView Answer on Stackoverflow
Solution 12 - DjangoSachinView Answer on Stackoverflow
Solution 13 - Djangoha22109View Answer on Stackoverflow