Getting django signals to work between applications - python

I have a Django app called receipt_printer which should be printing an order when it is received using a signal. There are two apps here called: Orders and receipt_printer.
However, whenever an order is processed and fully charged I can't get the signals to work. The example below just prints out something to the console for simplicity. If I do everything inside the models.py in the Orders app it works fine.
receipt_printer/apps.py
from django.apps import AppConfig
class ReceiptPrinterConfig(AppConfig):
name = 'receipt_printer'
def ready(self):
from . import signals
receipt_printer/signals.py
from django.db import models
from saleor.order.models import Order
from django.db.models.signals import post_save
def order_fully_paid_signal(sender, instance, created, **kwargs):
if instance.get_payment_status() == "fully-charged":
print("This signal is working for fully paid order")
else:
print("This signal won't working for fully paid order")
post_save.connect(order_fully_paid_signal, sender=Order)
receipt_printer/init.py
default_app_config = 'receipt_printer.apps.ReceiptPrinterConfig'
UPDATE, also tried the below - didn't work:
#receiver(post_save, sender=Order)
def order_fully_paid_signal(sender, instance, created, **kwargs):
if instance.get_payment_status() == "fully-charged":
print("This signal is working for fully paid order")
else:
print("This signal won't working for fully paid order")

Not entirely sure what the issue is here but below is a post_save pattern I use that does work cross-app without any changes needed to apps.py nor init.py:
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=<Model>, dispatch_uid='<model>_post_save')
def <model>_post_save(sender, instance, **kwargs):
...

I needed to add the following line to the bottom of my models.py for the Orders app:
from saleor.saleor_package.receipt_printer.signals import order_fully_paid_signal

Related

Sending and receiving signals in django models

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?
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
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.
Make sure you are using your app config in the __init__.py of you application default_app_config = 'myapp.apps.MyappConfig'
To connect to the signal you created try this in your signals.py file:
#receiver(my_signal)
def my_handler(name, **kwargs):
print(name)
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()

Django : An AppRegistryNotReady raised error when trying to connect a signal between different apps

I want to build an notification system in my django project. So I started to create an new app called notification. To create the notification I have to listen to the actions of the other models of my project. To reach this purpose I created in my notification app a signal handler :
in notification/signals.py
def create_subscription(sender, **kwargs):
pass
I connect this handler to my signal in my notification/apps.py
from django.apps import AppConfig
from django.db.models.signals import post_save
from notification.signals import create_subscription
from django.conf import settings
class NotificationConfig(AppConfig):
name = 'notification'
def ready(self):
post_save.connect(create_subscription, sender=settings.AUTH_USER_MODEL, dispatch_uid="create_subscription")
This works fine. I used my custom User model defined in my settings.
But whenever I want to use another model of my project, like :
from django.apps import AppConfig
from django.db.models.signals import post_save
from notification.signals import create_subscription
from member.models import Participation
class NotificationConfig(AppConfig):
name = 'notification'
def ready(self):
post_save.connect(create_subscription, sender=Participation, dispatch_uid="create_subscription")
I get an AppRegistryNotReady error, no matter which model I use.
I checked the order of declaration of my settings.INSTALLED_APPS, 'member' is declared before 'notification'.
When referring to the User model by passing threw the settings.AUTH_USER_MODEL it's working fine, but when referring directly to the model it creates an error.
Any ideas?
Although you can’t import models at the module-level where AppConfig classes are defined, you can import them in ready(), using either an import statement or get_model().
You need to do like
class NotificationConfig(AppConfig):
name = 'notification'
def ready(self):
from member.models import Participation
post_save.connect(create_subscription, sender=Participation, dispatch_uid="create_subscription")
For more info

django extend installed app model save method

Let's say i need to call my own function def do_stuff() after i save model. If that model would be in app which i have created, that would be no problem. I could do:
def save(self, *args, **kwargs):
super(Post, self).save(*args, **kwargs)
do_stuff()
But i need to call save() in 3rd party app when model is saved. I can think of only copying all project to my local directory and append save() method, but that is not nice since i have to copy all app code. Is there any nicer way to do this?
EDITED:
apps.py:
from django.apps import AppConfig
class SubscriptionConfig(AppConfig):
def ready(self):
import subscription.signals
signals.py:
from django.db.models.signals import post_save
from django.dispatch import receiver
from djangocms_blog.models import Post
#receiver(post_save, sender=Post)
def send_emails(instance, **kwargs):
print 'instance %s' %instance
__init__.py:
default_app_config = 'subscription.apps.SubscriptionConfig'
There are signals that are dispatched after some certain events. One of them, post_save (or pre_save if you want to do something just before the object is saved) would work in your case.
To be more specific, create a signals.py in your app's folder:
from django.db.models.signals import post_save
from django.dispatch import receiver
from your_project.your_app.models import YourModel
#receiver(post_save, sender=YourModel)
def do_stuff(instance, **kwargs):
# instance here is your object, you can use or modify it
instance.title = "New title"
# don't forget to save your object if you edit
instance.save()
And then make sure this signals.py is imported in somewhere. It is recommended to do it in app's apps.py:
from django.apps import AppConfig
class YourAppConfig(AppConfig):
name = 'your_projects.your_app'
verbose_name = "Your app's verbose name"
def ready(self):
from your_project.your_app import signals
As last step, make sure your app uses the AppConfig you defined there. Open app's __init__.py and put this:
default_app_config = 'your_projects.your_app.apps.YourAppConfig'
Now, every time the signal you selected dispatches, your handler function will be run.
You can use post_save signal. It will be invoked after model is saved.

in django 1.8, how to set sender for post_migrate and post_syncdb signal receiver when a custom user model is set?

Following is my code in the signals.py file placed in the package where the auth model is defined.
#receiver(post_migrate, sender=settings.AUTH_USER_MODEL)
def define_groups(sender, **kwargs):
# Create groups
Group.objects.get_or_create(name='Promoter')
Group.objects.get_or_create(name='Client')
Group.objects.get_or_create(name='Superuser')
Group.objects.get_or_create(name='Staff')
The documentation (https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#referencing-the-user-model) states that it should be set as
sender=settings.AUTH_USER_MODEL
while this only works for post_save as mentioned in the documentation example.
I've already tried get_user_model() and also directly using the my_custom_user.models.
get_user_model() returns an error, while setting models as sender works just fine, as -
from . import models
#receiver(post_syncdb, sender=models)
def define_groups(sender, **kwargs):
# Create groups
Group.objects.get_or_create(name='Promoter')
Group.objects.get_or_create(name='Client')
Group.objects.get_or_create(name='Superuser')
Group.objects.get_or_create(name='Staff')
But according to documentation this is not the right way to refer a custom user model and is just an ugly workaround.
Would someone please be able to help me with a solution so i can add these Groups with the first migration of user model.
Thank You
EDIT : using get_user_model() returns the following error -
django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.
The sender for the post_migrate method is never a model (custom or otherwise), it is the AppConfig instance for the app which was installed.
The docs give the following example for connecting your signal handler in the ready method.
from django.apps import AppConfig
from django.db.models.signals import post_migrate
def my_callback(sender, **kwargs):
# Your specific logic here
pass
class MyAppConfig(AppConfig):
...
def ready(self):
post_migrate.connect(my_callback, sender=self)
Similarly, the sender for post_sync_db signal (note the signal is deprecated) is the module containing the models which were installed.

Signals do not work after registering

So far I have signals.py with following content:
from django.db.models.signals import post_delete, post_save
from django.core.signals import request_finished
from django.dispatch import receiver
from students.models import Student
#receiver(post_save, sender=Student)
def track_saved_objects(sender, **kwargs):
print 'i am here'
new_instance = kwargs['instance']
print new_instance
#receiver(request_finished)
def my_callback(sender, **kwargs):
print("Request finished!")
And I can not get why signals do not register and nothing happening with this code, nothing printed that means my signals are inactive
In docs I've found something about AppConfig.ready() but still can not get where else should I register my signals
As e-nouri said in the comments, this information is in the docs. If you scroll down from the Connecting Receiver Functions section to the "Where should this code live?" note, you'll see they should live in the AppConfig as you alluded to in your question.
In your application, you'll need to create an apps.py file if you don't have one created already. In it you'll define the config for your app. Here's an example that includes the signals registration.
from django.apps import AppConfig
class ExampleAppConfig(AppConfig):
name = 'example'
verbose_name = "Example Application"
def ready(self):
# To avoid putting the signals code in the __init__.py file or
# models.py file, we import the signals module here.
# https://docs.djangoproject.com/en/1.7/topics/signals/#connecting-receiver-functions
from example import signals

Categories