7

In our project we use soft delete (is_deleted attribute on a model). Before creating a new model I want to first check if it was already exist and deleted and if so - set is_deleted=False and save the already exists model. So I decided to override the model 'save' method. (not the View Create since it should work not only for requests) The problem is that 'save()' is called for all actions (create, update, delete). Is there a way I can call the overridden method only on create?

class Model(BaseModel): title = models.CharField(max_length=200) identifier = models.CharField(max_length=15) def save(self, *args, **kwargs): try: existing_model = Model.objects.active_and_deleted().get( identifier=self.identifier) if self.is_deleted is False: existing_model.is_deleted = False existing_model.title = self.title existing_model.created_at = self.created_at existing_model.updated_at = self.updated_at existing_model.created_by = self.created_by existing_model.deleted_at = None super(Model, existing_model).save(args, kwargs) except Model.DoesNotExist: # in case the Nodel wasn't already exist - continue pass super(Model, self).save(*args, **kwargs) 
4
  • You want to "delete" the record before inserting it again? Why? Commented Apr 26, 2018 at 19:54
  • So actually you delete nothing in your db! You deal with all the objects that have is_deleted=False. when someone deletes something, you just turn is_deleted to True. That's right? Commented Apr 26, 2018 at 20:01
  • @Lemayzeur - That's true. Commented Apr 26, 2018 at 20:50
  • Before creating a new model I want to first check if it was already exists. for that you need a field as comparisation to test whether the object already exist or not, for instance: you can check with title Commented Apr 26, 2018 at 21:30

3 Answers 3

15

The answer to your question, "Is there a way I can call the overridden method only on create?" is this:

 def save(self, *args, **kwargs): if not self.id: # Object is a new instance return super(Model, self).save(*args, **kwargs) 

But there are other issues here. Your model shouldn't be named "Model", for example.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, but it does have id when first getting to the save function. I'm using Model.objects.create(...) to test it.
I think you're mistaken. If the item hasn't been created, then it will not have an id or pk. That's a core component of the way the ORM works.
That answer applies when "the object has a UUIDField as its primary_key". It doesn't appear that you are doing that, or manually setting the pk. Are you?
Instead of "if not self.id", try "if self._state.adding".
@PaulTuckett - another reason to use self._state.adding is if you are using the model field keyword "primary_key". In that case self does not have the "id" attribute and self.pk is assigned a value even before the model is saved.
3

Check whether self.pk is None or not could do what you want, here is an example:

class ModelName(mdoels.Model): title = models.CharField(max_length=200) identifier = models.CharField(max_length=15) def save(self, *args, **kwargs): if self.pk is None: # Adding of a new instance # let's say we compare instances by `title` # you can use other fields for the comparison possible_old_instance = Model.objects.filter(title__iexact=self.title) if possible_old_instance.exists(): existing_model = possible_old_instance.first() existing_model.is_deleted = False existing_model.title = self.title existing_model.created_at = self.created_at existing_model.updated_at = self.updated_at existing_model.created_by = self.created_by existing_model.deleted_at = None existing_model.save() # Nothing happens, we don't call save() method else: # in case the Model doesn't exist super(ModelName, self).save(*args, **kwargs) else: super(ModelName, self).save(*args, **kwargs) 

2 Comments

Im my project self.pk is already set for a new instance also. But I will use different attributes of self to identify different actions. Thanks
You can do so, with self.pk or self.id... these attributes only return non-null None value once save() 's called
3

In case when you are using the UUID field instead of the default ID field, check self.pk field is a terrible idea because UUID is assigned as default before we go to the save method. Well, how to resolve it? The simplest and fastest solution is self._state.adding instead of self.pk

I hope you noticed an underscore before the "state" word, but don't worry, this method is completely legal and we do not call here a private variable.

Example:

def save(self, *args, **kwargs): if self._state.adding: # will return true if obcject is newly created # Object is a new instance return super(Model, self).save(*args, **kwargs) 

1 Comment

This is the better solution. Works in all cases.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.