Sending and receiving signals in django models
Asked Answered
K

3

8

I am using django 2.0.8 and Python 3.5. I want to be able to send and receive custom signals when an object is saved to the database.

I have followed the Django documentation on listening to signals and also the core signals bundled with Django - however, I am unable to get my example to work.

This is what I have so far:

myapp/models.py

from django.db import models
import django.dispatch

my_signal = django.dispatch.Signal(providing_args=["name"])

class Foo(models.Model):
    name = models.CharField(max_length=16)

def save(self, *args, **kwargs):
    try:
        # Call the "real" save() method.
        super().save(*args, **kwargs)  

        # Fire off signal         
        my_signal.send(sender=self.__class__, name=self.name)

    except Exception as e:
        print ('Exception:', e)
        #pass

myapp/signals.py

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


@receiver(post_save, sender=Foo)
def foo_handler(sender, **kwargs):
    print('Foo Signal Recieved!')
    print(kwargs)

myapp/app.py

class MyappConfig(AppConfig):
    name = 'myapp'
    label = 'myapp'

    def ready(self):
        import myapp.signals

Sample usage

from myapp.models import Foo

foo = Foo(name='Homer Simpson')
foo.save() # Object is saved, but event is not fired!

Can anyone explain why the signal is not being fired?

Kerguelen answered 31/8, 2018 at 10:32 Comment(11)
You could get create,update and delete informations accurately (I persume).Nominee
What errors are you getting? Have you tried a print('signal received') in your code to check that it's called?Symphysis
To see the format of the fixture, I would create some bogus data in code, then use manage.py dumpdata (docs.djangoproject.com/en/2.0/howto/initial-data/…) to get a dump that you can then modify.Symphysis
what happens in fooch_save when you update the table FoodooChile? You should add logging to that function and run FoodooChile.objects.create(name='test') in the shellSister
@LaurentS thanks for that suggestion. I will remove that from my question and focus my question on solely how to send/receive signals.Kerguelen
You are not trying to connect to he right signal. I can only see a receiver on the post_save signalSkiascope
@GharianiMohamed I don't understand. The code above is drawn from the Django signals documentation. What is missing? Care to clarify some more - perhaps in an answer?Kerguelen
An important Note, how did you run the code in Sample usage?? If you run this in python manage.py shell, you need import the signal.py first .otherwise, signal won't work.Mahound
Can you include your myapp.__init__.py and your settings.py files?Skiascope
do you set default_app_config in your myapp/__init__.py file? default_app_configMahound
Please edit your question and show your INSTALLED_APPS from settings.py, specifically the part where you include myapp.Tarpan
M
8

It seems you need two features supplied by Django. signal and contenttypes.

So read the doc first

The model Activity is related to contenttypes,seems you miss object_id Field, which indicate which model instance is being crud.

For every crud action, An Activity instance is being created.This part is just the code written in signal.py

signal: signal have to connect each concrete model. Fortunately,See the source code of decorator receiver.

We have a signal list [post_save,post_delete] and a model list (FoodooChile, FooBarChile) to connect .

In post_save,argument created indicate the action is create or update.

At last, Usually we import the signal file in urls.py, maybe not the best practice.


It is also related to your settings.py. use 'myapp.apps.MyappConfig' replace myapp in settings.py,or define default_app_config = 'myapp.apps.MyappConfig' in myapp/__init__.py. The link above in comments describe this in detail

Mahound answered 4/9, 2018 at 20:21 Comment(0)
S
1
  1. In the myapp.signals you have a receiver that handels the post_save signal (@receiver(post_save, sender=Foo)) it doesn't connect to your signal.
  2. Make sure you are using your app config in the __init__.py of you application default_app_config = 'myapp.apps.MyappConfig'
  3. To connect to the signal you created try this in your signals.py file:

    @receiver(my_signal)
        def my_handler(name, **kwargs):
            print(name)
    
Skiascope answered 6/9, 2018 at 12:27 Comment(1)
Your suggested edits are different from what the official documentation states (especially regarding point 3). With point 2, this is no longer recommended for Django 2.0. I still did try the suggestions you made, however, the receiver function was not called.Kerguelen
A
1

You are reinventing the wheel, but only putting it on one side of the cart, so to speak.

the post_save signal is always sent on save, so defining your own signal is overkill. I know you got the argument there, but the receiver has the sender argument already, which is the saved object, so you can just do sender.name and you got the value you need.

Apart from that, you have a syntax error, your custom save function for your model is not indented. I don't know if this is a formatting error in your question or if that is how it looks in your code. Either way, should work if you just drop your custom signal.

Model

from django.db import models
import django.dispatch


class Foo(models.Model):
    name = models.CharField(max_length=16)

Signals

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


@receiver(post_save, sender=Foo)
def foo_handler(sender, **kwargs):
    print(sender.name)

App

class MyappConfig(AppConfig):
    name = 'myapp'
    label = 'myapp'

    def ready(self):
        import myapp.signals

Sample

from myapp.models import Foo

foo = Foo(name='Homer Simpson')
foo.save()
Ashtonashtonunderlyne answered 9/9, 2018 at 11:2 Comment(2)
I had a feeling that writing my own save signal was overkill, but I wanted to kill two birds with the same stone - i.e. 1. send/recieve a custom signal (so I could use the logic elsewhere (i.e. for different events) 2. get alerted on object being saved to db. Could you post an answer with my code corrected to do that? as it stands, I still don't see how what I'm doing is different from what the Django documentation recommends.Kerguelen
@HomunculusReticulli hope it helps!Ashtonashtonunderlyne

© 2022 - 2024 — McMap. All rights reserved.