Cancel saving model when using pre_save in django

14,376

Solution 1

See my another answer: https://stackoverflow.com/a/32431937/2544762

This case is normal, if we just want to prevent the save, throw an exception:

from django.db.models.signals import pre_save, post_save

@receiver(pre_save)
def pre_save_handler(sender, instance, *args, **kwargs):
    # some case
    if case_error:
        raise Exception('OMG')

Solution 2

I'm not sure you can cancel the save only using the pre_save signal. But you can easily achieve this by overriding the save method:

def save(self):
    if some_condition:
        super(A, self).save()
    else:
       return   # cancel the save

As mentioned by @Raptor, the caller won't know if the save was successful or not. If this is a requirement for you, take look at the other answer which forces the caller to deal with the "non-saving" case.

Solution 3

If the data's always coming from a Form and you have a straightforward test for whether or not the save should occur, send it through a validator. Note, though, that validators aren't called for save() calls originating on the backend. If you want those to be guarded as well, you can make a custom Field, say class PrimeNumberField(models.SmallIntegerField) If you run your test and raise an exception in the to_python() method of that custom field, it will prevent the save. You can also hook into the validation of a specific field by overriding any of several other methods on the Field, Form, or Model.

Share:
14,376

Related videos on Youtube

Alfred Huang
Author by

Alfred Huang

一切有为法, 如梦幻泡影。 如露亦如电, 应作如是观。

Updated on June 06, 2022

Comments

  • Alfred Huang
    Alfred Huang about 2 years

    I have a model:

    class A(models.Model):
        number = models.IntegerField()
    

    But when I call A.save(), I want to ensure that number is a prime (or other conditions), or the save instruction should be cancelled.

    So how can I cancel the save instruction in the pre_save signal receiver?

    @receiver(pre_save, sender=A)
    def save_only_for_prime_number(sender, instance, *args, **kwargs):
        # how can I cancel the save here?
    
    • AlvaroAV
      AlvaroAV about 10 years
      You have to overwrite the save function of the model, as @Sebastien said
  • Alfred Huang
    Alfred Huang almost 9 years
    After a long time of practice, I found making a validator to restrict the field and always save before full_clean is a good architecture. You solution is the most enlightening.
  • Raptor
    Raptor over 7 years
    This looks good however the caller has not idea that the save didn't work!
  • Alfred Huang
    Alfred Huang about 7 years
    Be aware that if you use use update on a queryset, this hook is not triggered. e.g. MyModel.objects.filter(some_case=1).update(someval=2) Just prevent to use it.
  • TheJKFever
    TheJKFever about 7 years
    @AlfredHuang, any way to raise an error when updating a queryset?
  • Alfred Huang
    Alfred Huang about 7 years
    @TheJKFever AFAK, there is no straight forward solution, because queryset update will use one sql update on many rows. If you want to do so, traverse the queryset and update it one by one.
  • Vishal Rao
    Vishal Rao over 5 years
    I think you could handle it with the post_save signal for updates since that's called on update. So a combination of the two signals would probably be the right answer here.
  • Silko
    Silko over 5 years
    Is there a way to set the condition for saving in pre_save?
  • David Schumann
    David Schumann over 4 years
    yes, instead of return an exception should be raised.
  • Jayesh
    Jayesh about 4 years
    I need to do this but raising exception breaks the flow. I don't want to break the flow. Also I can't override save method or use post_save.
  • justin
    justin over 3 years
    Where's the best place to catch the exception raised in pre_save? I want to prevent the save, but don't want to result in a 500 error.