240

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?

phoenix
  • 7,988
  • 6
  • 39
  • 45
MikeN
  • 45,039
  • 49
  • 151
  • 227
  • 2
    Please select https://stackoverflow.com/a/35647389/8893667 as the correct answer. The answer doesn't work in many cases like a `UUIDField pk` – Kotlinboy Feb 10 '18 at 10:09

14 Answers14

352

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

Emilio Conte
  • 1,105
  • 10
  • 29
SaintTail
  • 6,160
  • 4
  • 31
  • 51
  • 25
    This is the only correct way when using a custom primary key field. – webtweakers Feb 29 '16 at 10:09
  • 15
    Not sure about all the details of how `self._state.adding` works, but fair warning that it seems to always equal `False` if you're checking it _after_ calling `super(TheModel, self).save(*args, **kwargs)`: https://github.com/django/django/blob/stable/1.10.x/django/db/models/base.py#L799 – agilgur5 Jun 26 '16 at 05:52
  • 2
    This is the correct way and should be upvoted/set as the correct answer. – flungo Jan 05 '17 at 18:25
  • 2
    You should always be careful when using private attributes or methods from Django though, so even if it works it might not be the best solution – gdvalderrama Jan 25 '17 at 15:38
  • 13
    @guival: `_state` isn’t private; like `_meta`, it’s prefixed with an underscore to avoid confusion with field names. (Notice how it’s used in the linked documentation.) – Ry- Apr 25 '17 at 01:52
  • 8
    This is the best way. I used `is_new = self._state.adding`, then `super(MyModel, self).save(*args, **kwargs)` and then `if is_new: my_custom_logic()` – kotrfa Aug 28 '17 at 08:13
  • this is a great way to overide django admin's save models methods. thanks.. –  Dec 29 '18 at 07:21
  • 2
    The real correct answer. Some times you can create an object setting an ID, so check for pk is None will not work. – Gustavo Gonçalves Jan 12 '20 at 21:31
  • I recently observed a case where someone took an existing model instance, set `pk` to `None` (django way of copying a model) and `self._state.adding` was `False` during `save`. So probably both `_adding` and `pk` should be checked to cover all possible cases. – Spidey Feb 27 '23 at 11:43
244

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.

Dave W. Smith
  • 24,318
  • 4
  • 40
  • 46
  • The corner case of save failure can be dealt with by wrapping the super() call in a try-except block and doing any necessary cleanup for the "special action". – Carl Meyer May 26 '09 at 18:32
  • 21
    You should use `is not` rather than `!=` when checking for identity with the `None` object – Ben James Dec 04 '09 at 10:38
  • 3
    Not all models have an id attribute, i.e. a model extending another through a `models.OneToOneField(OtherModel, primary_key=True)`. I think you need to use `self.pk` – AJP Apr 09 '13 at 10:21
  • @AJP, you're right. You should always use `pk` rather than `id`, especially if you aren't certain whether `id` exists in your model or has been overridden. This happens if you have any field with `primary_key=True` or any field named `id` that isn't the PK. – hobs May 17 '13 at 19:36
  • 5
    This MAY NOT WORK in some cases. Please check this answer: http://stackoverflow.com/a/940928/145349 – fjsj Mar 05 '15 at 16:37
  • 12
    This is not the correct answer. If using a `UUIDField` as a primary key, `self.pk` is never `None`. – Daniel van Flymen Mar 04 '16 at 22:53
  • 1
    Side note: This answer pre-dated UUIDField. – Dave W. Smith Aug 09 '16 at 02:32
  • This is the correct answer https://stackoverflow.com/a/35647389/8893667 The given answer is unreliable at times and as mentioned above when you use `UUIDField` – Kotlinboy Feb 10 '18 at 10:02
  • This will also NOT work when you are saving a multi-table Child Model Class. The parent instance is created first before saving the child instance. And by default, the pk for the child instance is attained from the parent instance's pk. – kiiru May 02 '18 at 04:03
  • The primary **readable way** I think should be: ```self._state.adding is True``` - ***creating*** / ```self._state.adding is False``` - ***updating*** – Alexandr S. Apr 16 '19 at 11:46
  • self._state.adding is the true way to go, in any case will work. – Gustavo Gonçalves Jan 12 '20 at 21:33
  • 2
    >`"self._state is not a private instance variable, but named that way to avoid conflicts"` is there a source for this please? @DaveW.Smith – Tomiwa Dec 18 '20 at 23:52
52

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

Gerry
  • 1,546
  • 10
  • 5
43

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.

KayEss
  • 2,290
  • 15
  • 31
  • Good point! I can get away with this in my application (checking for None primary key) because I never set the pk for new objects. But this would definitely not be a good check for a reusable plugin or part of the framework. – MikeN Jun 03 '09 at 13:50
  • 1
    This is specially true when you assign the primary key yourself and and through the database. In that case the surest thing to do is to make a trip to the db. – Constantine M Dec 10 '12 at 11:31
  • 1
    Even if your application code does not specify pks explicitly the fixtures for your test cases might. Though, as they are commonly loaded before the tests it might not be a problem. – Risadinha May 07 '14 at 11:07
  • 1
    This is especially true in the case of using a `UUIDField` as a Primary Key: the key isn't populated at the DB level, so `self.pk` is always `True`. – Daniel van Flymen Mar 04 '16 at 22:52
  • This solution also works for this case: https://stackoverflow.com/a/35647389/4379151 – Erik Kalkoken Jan 19 '22 at 15:18
12

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

Tim Tisdall
  • 9,914
  • 3
  • 52
  • 82
JF Simon
  • 1,235
  • 9
  • 13
  • 8
    That can potentially cause race conditions if there's a lot of load. That's because the post_save signal is sent on save, but before the transaction has been committed. This can be problematic and can make things very difficult to debug. – Abel Mohler Nov 30 '12 at 21:20
  • I'm not sure if things changed (from older versions), but my signal handlers are called within the same transaction so a failure anywhere rolls back the whole transaction. I'm using `ATOMIC_REQUESTS`, so I'm not really sure about the default. – Tim Tisdall Oct 31 '17 at 19:26
7

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

Kwaw Annor
  • 1,448
  • 13
  • 13
7

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

Jordan
  • 1,969
  • 2
  • 18
  • 19
6

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 :)

metakermit
  • 21,267
  • 15
  • 86
  • 95
3

rather use pk instead of id:

if not self.pk:
  do_something()
yedpodtrzitko
  • 9,035
  • 2
  • 40
  • 42
1
> 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)
  • Using cleaned_data.get(), I was able to determine whether I had an instance, I also had a CharField where null and blank where true. This will update at each update according to the logged in user – Swelan Auguste Apr 05 '20 at 21:28
1

It is the common way to do so.

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

vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
0

Would this work for all the above scenarios?

if self.pk is not None and <ModelName>.objects.filter(pk=self.pk).exists():
...
Sachin
  • 176
  • 1
  • 6
0

In python 3 and django 3 this is what's working in my project:

def save_model(self, request, obj, form, change):
   if not change:
      #put your code here when adding a new object.
user3486626
  • 139
  • 1
  • 6
-3

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

Alex L
  • 8,748
  • 5
  • 49
  • 75
ha22109
  • 8,036
  • 13
  • 44
  • 48