22

Maybe it's just late, but I cannot figure out why this isn't working. When I have a post_save signal call a generic function, it works, but when I have a post_save signal call a method from a model, nothing happens. Here is code that works:

class Revision(models.Model):
    # Model junk...

def send_email(sender, instance, created, **kwargs):
    if created:
        print "DO STUFF"

signals.post_save.connect(send_email, sender=Revision)

But this does not work:

class Revision(models.Model):
    # Model junk...

    def send_email(sender, instance, created, **kwargs):
        if created:
            print "DO STUFF"

signals.post_save.connect(Revision.send_email, sender=Revision)

Is there a kind soul out there who will keep me from smashing my head into the wall? Thanks.

winduptoy
  • 5,366
  • 11
  • 49
  • 67

1 Answers1

33

It seems to me that the problem in the second one is you are using an unbounded method send_mail. If you really want to call send_mail from within a class, maybe @classmethod or @staticmethod will help you out:

class Revision(models.Model):
    # Model junk...

    @classmethod
    def send_email(cls, sender, instance, created, **kwargs):
        if created:
            print "DO STUFF"

signals.post_save.connect(Revision.send_email, sender=Revision)

or

class Revision(models.Model):
    # Model junk...

    @staticmethod
    def send_email(sender, instance, created, **kwargs):
        if created:
            print "DO STUFF"

signals.post_save.connect(Revision.send_email, sender=Revision)

Alternatively without using these decorators, you can pass the bounded instance method:

class Revision(models.Model):
# Model junk...

    def send_email(self, sender, instance, created, **kwargs):
        if created:
            print "DO STUFF"

signals.post_save.connect(Revision().send_email, sender=Revision)

References:

  1. From the Django source code:

    def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
        """
        Connect receiver to sender for signal.
    
        Arguments:
    
            receiver
                A function or an instance method which is to receive signals.
                Receivers must be hashable objects.
    
                If weak is True, then receiver must be weak-referencable (more
                precisely saferef.safeRef() must be able to create a reference
                to the receiver).
    
                Receivers must be able to accept keyword arguments.
    
                If receivers have a dispatch_uid attribute, the receiver will
                not be added if another receiver already exists with that
                dispatch_uid.
    
  2. Difference between @classmethod and @staticmethod: What is the difference between @staticmethod and @classmethod in Python?

Community
  • 1
  • 1
K Z
  • 29,661
  • 8
  • 73
  • 78
  • Your third example using the instance method, the instance being called here is a new one, right, not the instance that was —here— saved? Edit: Oh but the `instance` variable *is* the one being saved. Seems a bit silly to do it like that, but I see that you're just showing you *can* do that if you need to. Cool. – Oli Sep 23 '16 at 12:01
  • And how do you do that with `receiver` decorator? – Shiplu Mokaddim Mar 26 '21 at 16:50
  • I don't think that third example works. From my testing, instance methods are not the same across all instances – smac89 Aug 04 '21 at 18:03