I am trying to use signals for the first time. I have a model, UserAlias, and I want to execute some code after a UserAlias record is created.
UserAlias is defined in aliases/models.py
I created a aliases/signals/handlers.py. Here is that file's contents:
from django.db.models.signals import post_save, post_init, post_delete
from django.dispatch import receiver
from aliases.models import UserAlias
#receiver(post_init, sender=UserAlias)
def post_init_handler(sender, **kwargs):
print('hello there from a signal')
#receiver(post_save, sender=UserAlias)
def post_save_handler(sender, **kwargs):
print('hello there from a signal')
#receiver(post_delete, sender=UserAlias)
def post_delete_handler(sender, **kwargs):
print('hello there from a signal')
But when I execute:
from aliases.models import *
newalias = UserAlias.objects.create(...omitted...)
I do not see any of my debug print statements execute.
What am I missing here?
UPDATED:
I moved the definitions of my post_init_handler, post_save_handler and post_delete_handler to my aliases/models.py file after the declaration of my UserAlias class.
Also my post_init_handler now looks like this:
#receiver(post_init, sender=UserAlias)
def post_init_handler(sender, instance, **kwargs):
print(f'hello there from a signal {sender} {instance}')
I tried declaring it as Paaksing suggested ...
#receiver(post_init, sender=UserAlias)
def post_init_handler(sender, instance, created, **kwargs):
print(f'hello there from a signal {created} {sender} {instance}')
But I would get this error:
TypeError: post_init_handler() missing 1 required positional argument: 'created'
UPDATE II:
The post_init handler does not have a created parameter. The created parameter goes with the post_save handlers. Like this:
#receiver(post_save, sender=UserAlias)
def post_save_handler(sender, instance, created, **kwargs):
if created:
print(f'hello from post_save_handler( {sender}, {instance}, {created})')
You missed a created attr.
Consider adding instance if working with models that are user-like so you can manipulate them on the signals.
#receiver(post_save, sender=UserAlias)
def post_save_handler(sender, instance, created, **kwargs):
if created:
print('hello there from a signal')
Update: As Red Cricket said, the post_init does not have a created instance, but works with post_save as expected.
In the django doc shows all the instances that you can work with each signal: https://docs.djangoproject.com/en/3.0/ref/signals/
You created your signals in a seperate module (which is good). You also need to import the module at the right direction to use them.
The Django documentation advices to put the signals inside your app config file.
You could do it for example like this.
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'myproject.app'
def ready(self) -> None:
import myproject.app.aliases/signals/handlers # noqa
Related
From django docs it is clear that we can write signals handlers as function.
from django.db.models.signals import pre_save
from django.dispatch import receiver
from myapp.models import MyModel
#receiver(pre_save, sender=MyModel)
def my_handler(sender, **kwargs):
...
Is it possible to write the signal handlers as classes?
If yes HOW?
YourClass(Inheritance):
#receiver(pre_save, sender=MyModel)
def __call__(self, sender, **kwargs):
#your code
return
And then when you want to use this class you would import it and write:
my_handler = YourClass()
So you then could include an instantiated version in your settings file or wherever you need it.
Something like this should be possible. But why may I ask?
I'm working on a script which finds all Django pre_save and post_save signals and comments overwritten save methods so programmer is aware of all signals affecting the workflow.
For Example
There are two receivers:
#receiver(pre_save, sender=MyModel)
def sig_mymodel_pre_save(instance, sender, **kwargs):
...
#receiver(post_save, sender=MyModel)
def sig_mymodel_post_save(instance, sender, created, **kwargs):
...
And model MyModel:
class MyModel(..):
...
def save(self,*args,**kwargs):
...
super().save(...)
....
I want the script to modify MyModel code to look like this:
class MyModel(..):
...
def save(self,*args,**kwargs):
...
# SIGNAL pre_save | myapp.models.sig_mymodel_pre_save
super().save(...)
# SIGNAL post_save | myapp.models.sig_mymodel_post_save
....
So the first thing I'm going to do is to list all receivers of post_save and pre_save signals:
def get_signals() -> {}:
result = {}
for signal in [pre_save, post_save]:
result[signal] = signal.receivers
return result
def comment_signals_to_save_methods():
for signal, receivers in get_receivers():
for receiver in receivers:
models = ???
And here is the problem - I can't get models for the receiver. There is no such method or attribute.
Do you know how to do it?
I'm creating an instance of a model (Container), and it seems like the pre_save function is not triggered.
This is the class in the 'signals':
class ContainerCreatedMixin(object):
#staticmethod
#receiver(pre_save, sender=Container)
def container_pre_save(sender, instance, **kwargs):
# create container folder if not created yet
if instance.folder_created_at is None:
is_folder_created = ContainerCreatedMixin().create_folder(instance)
if is_folder_created:
instance.folder_created_at = now()
def create_virtual_folder(self, container):
try:
......
Using the receiver decorator on a class method doesn't really make sense.
Put your decorated method out of class and it should be registered if the file is imported. Also, there is no need of creating Mixings for the following.
I have separate python module for signal receivers, it's called signals.py and imported in ready() method of my AppConfig.
In this module I implemented post_save and post_delete signal receivers for specific model and registered them via decorator:
#receiver(post_save, sender=MyModel)
def generate_smth(sender, instance, created, **kwargs):
...
And it works fine.
But, when I added to signals.py receivers of same signals in the same manner, but from different specific models:
#receiver(post_save, sender=AnotherModel)
def generate_smth(sender, instance, created, **kwargs):
...
My functions stopped to receive signals. But if I move receivers into separate python modules mymodel_signals.py and anothermodel_signals.py and import both modules in ready() then all of them works again.
Why it isn't possible to keep receivers in one module?
#receiver(post_save, sender=MyModel)
#receiver(post_save, sender=AnotherModel)
def generate_smth(sender, instance, created, **kwargs):
if sender.__name__ = 'MyModel':
# Bar
else:
# Foo
Do you want both functions to have the same behaviour? If yes, you can do:
def do_smth(sender, instance, created, **kwargs):
...
#receiver(post_save, sender=MyModel)
def generate_smth(sender, instance, created, **kwargs):
do_smth(sender, instance, created, **kwargs)
#receiver(post_save, sender=AnotherModel)
def generate_another_smth(sender, instance, created, **kwargs):
do_smth(sender, instance, created, **kwargs)
I'm trying to register multiple signals on one model. It seems that as I register an additional signal, it removes the previous signal.
from django.dispatch import receiver
from django.db.models.signals import post_save,post_delete
from my.app.models import Resource
#receiver(post_save,sender=Resource)
def ResourceSaved(sender,**kwargs):
print "Saved"
#receiver(post_delete,sender=Resource)
def ResourceSaved(sender,**kwargs):
print "Deleted"
I've taken a look around the docs, but I keep finding details on how to create custom signals in a class formate. Not how to register in class format.
I would imagine I can do something like this:
#reciver(sender=Resource)
class SignalAnsweringMachine(object):
def post_delete(self,**kwargs):
print "delete"
def post_save(self,**kwargs):
print "save"
Thanks for your help in advance.
You are (probably unintentionally) redefining ResourceSaved. Try this instead:
#receiver(post_save,sender=Resource)
def ResourceSaved(sender,**kwargs):
print "Saved"
#receiver(post_delete,sender=Resource)
def ResourceDeleted(sender,**kwargs):
print "Deleted"
Since the merge of #18454 released in Django 1.5 you can connect more than one signal by supplying a list of signals:
#receiver([post_save, post_delete], sender=Resource)
def ResourceSaved(sender, **kwargs):
pass
I use this short form to register two or more handlers.
In my case I clear cached categories list.
from django.dispatch import receiver
from django.db.models.signals import post_save, post_delete
from ..models import Category
all_categories = []
# Post save handler for Category model to clear "all_categores" variable
#receiver(post_save, sender=Category)
#receiver(post_delete, sender=Category)
def post_save_category(sender, **kwargs):
# clean cached categories
global all_categories
all_categories = []