Send Custom Parameters to Django Signals - python

I am working on Django Signals to handle data in Redis whenever any change happens in the Postgres database. But, I am unable to send custom parameters to Signal Receiver. I have gone through a lot of questions, but I am not able to understand how to send extra custom parameters to Signal Receiver.
Usually I do,
#receiver(post_save, sender=post_data)
def addToRedis(sender, instance, **kwargs):
But I want to do,
#receiver(post_save, sender=post_data)
def addToRedis(sender, instance, extra_param=extra_param_data **kwargs):
# Get `extra_param`
Here, I want to read extra_param to store the data in Redis.
I am using Django Rest Framework. And post_save is directly called after serializer.save()
It'll be great if someone can help me out in this.

You can send any additional parameters in a signal as keyword arguments:
#receiver(post_save, sender=post_data)
def addToRedis(sender, instance, **kwargs):
# kwargs['extra_param']
How to send:
my_signal.send(sender=self.__class__, extra_param='...')
If you have no access to the signal send function (eg REST framework internals), you can always use a custom signal.

This Answer the with Respect to Django Rest:
in your views.py
my_obj = Mymodel()
my_obj.your_extra_param = "Param Value" # your_extra_param field (not Defined in Model)
my_obj.save()
post_save.connect(rcver_func,sender=Mymodel,weak=False)
and define a signal.py with following
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Mymodel
#receiver(post_save,sender=Mymodel)
def rcver_func(sender, instance, created,**kwargs):
if created:
received_param_value = instance.your_extra_param # same field as declared in views.py

Related

Manage tow Signals in two different apps in Django

i have two Django applications, blogApp and accounts, when i created a signal file for blogapp that can slugify title after saving the model into database, this works perfectly.
But when i added the second signal file to accounts that can create profile to the user when he finished his registration, it shows me this error:
, and when i check the admin section, i can see the profile has been successfully created.
PostModel in blogApp application:
Signals in blogApp application:
ProfileModel in accoounts application:
Signals in accounts application:
So, how can i create the user profile without indexing to Post signals.
Because what i'm thinking is the two signals of two apps is activating after the user press register.
I think, your problem is with the sender you have set.
You want to make a specific action about a Post instance,but you set User as sender ?
So in your receive function, you try to get a Post instance with as id the id of the user provided as isntance.
#receiver(post_save, sender=Post)
def slugify_title_model_field(sender, instance, created, **kwargs):
if created:
post_to_slugify = Post.objects.get(id=instance.id)
post_to_slugify.title = slugify(post_to_slugify.title)
post_to_slugify.slug = slugify(post_to_slugify.title)
post_to_slugify.save()
Of course, you have to remove the post_save.connect... write after this code.
But for this case, I advise you to overload the save function of your model, it is there for that, and it would be much more logical to put this function in a model because it directly concerns the instance being saved. I use signals to deal with cases external to the model itself in general.
class Post(models.Model):
...
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super().save(*args, **kwargs)

Custom user_logged_in signal BEFORE django updates last_login

I'd like to show a notification to user with some stats (e.g how many items have been sold since last time he logged in)
#receiver(user_logged_in)
def notify_user_on_login(sender, request, user, **kwargs):
items = Item.objects.filter(status=Item.HISTORY_STATUS, seller=user, when_trading__gte=user.last_login)
However, in this signal last_login has already been updated.
According to the source at django.contib.auth django also connects signal with function that updates last_login:
user_logged_in.connect(update_last_login)
Is it possible to call my function BEFORE updating? Or get same result without adding custom field or doing some strange magic?
The last_login is also updated with a handler to that signal, which is surely registered and executed before yours. You might be able to solve your issue by moving your app over django.contrib.auth in INSTALLED_APPS.
Signal handlers depending on order doesn't seem like a good idea though. So I would probably replace Django's handler with your own:
from django.contrib.auth.models import update_last_login
def notify_user_on_login(user):
items = Item.objects.filter(status=Item.HISTORY_STATUS, seller=user, when_trading__gte=user.last_login)
#receiver(user_logged_in)
def after_user_logged_in(sender, user, **kwargs):
notify_user_on_login(user)
update_last_login(sender, user, **kwargs)
user_logged_in.disconnect(update_last_login)

Django 1.8: Function not called on Signal

I'm picking up on a code which should send a signal every time a user logs in. Thats not happening though. The function get_create_stripe() isnt getting called when the user logs in.
Anyone can tell whats wrong?
I'm working in Django 1.8 and the whole code is here.
Gist about the code: This code is part of an e-commerce site which users stripe as its payment gateway. Intent is, every time user logs in, we create a new stripe id or return an existing one.
Is it because this function is not in models.py? This is written to a file 'signals.py' and I'm not quite sure how Django should understand to call get_create_stripe() from a signal call in this file. Is it so?
import stripe
from django.conf import settings
from django.contrib.auth.signals import user_logged_in
from .models import UserStripe
stripe.api_key = settings.STRIPE_SECRET_KEY
def get_create_stripe(sender, user, *args, **kwargs):
new_user_stripe, created = UserStripe.objects.get_or_create(user=user)
print "hello"
if created:
customer = stripe.Customer.create(
email = str(user.email)
)
print customer
new_user_stripe.stripe_id = customer.id
new_user_stripe.save()
user_logged_in(get_create_stripe)
You need to connect your signal method to the signal.
Something like
from django.dispatch import receiver
from django.contrib.auth.signals import user_logged_in
#receiver(user_logged_in, sender=UserStripe)
def get_create_stripe(sender, user, *args, **kwargs):
EDIT: Also, what is this:
user_logged_in(get_create_stripe)
That is not how signals work. Either you do what I wrote above, or do this:
user_logged_in.connect(get_create_stripe)

How do I set a default group with django-authopenid?

I have a django app that uses django-authopenid as the sole registration method. I have registration in my installed apps, which django-authopenid uses. An ideal solution would allow me to run arbitrary code on the user object when they register. I can't directly modify the code for django-authopenis or registration.
Let me know if I need to add any more details.
On models.py you could bind the post_save signal:
from django.contrib.auth.models import User, Group
from django.db.models.signals import post_save
def default_group(sender, instance, created, **kwargs):
if created:
instance.groups.add(Group.objects.get(name='your default group name'))
post_save.connect(default_group, sender=User)
If in doubt, read the signals documentation.

Why can I access an object during it's post_save Signal, but not when I trigger code within that signal that calls it on another process

All, I've got a issue with django signals.
I have a model
In an effort to speed up responsiveness of page loads, I'm offloading some intensive processing that must be done, via a call to a second localhost webserver we're running, both using the same database. I'm seeing behavior where the calling process can retrieve the object, but the called process can't. Both port 80 and port [port] are pointing to django processes running off the same database.
In models.py
class A(models.Model):
stuff...
def trigger_on_post_save( sender, instance, create, raw, **keywords):
#This line works
A.objects.get( pk=instance.pk )
#then we call this
urlopen( r'http://127.0.0.1:[port]' +
reverse(some_view_url, args(instance_pk) ).read()
post_save.connect( trigger_on_post_save, A )
In views.py
def some_view_function( request, a_pk ):
#This line raises an object_not_found exception
A.objects.get( pk=a_pk )
Furthermore, after the urlopen call raises an exception, the object does not exist in the database. It was my understanding that post_save was called after the object had been saved, and written to the database. Is this incorrect?
We ran into a similar issue and we ended up using on_commit callback (NOTE: This is only possible with Django >= 1.9). So, you could possible do something like:
from django.db import transaction
class A(models.Model):
stuff...
def trigger_on_post_save( sender, instance, create, raw, **keywords):
def on_commit():
urlopen(r'http://127.0.0.1:[port]' +
reverse(some_view_url, args(instance_pk) ).read()
transaction.on_commit(on_commit)
post_save.connect( trigger_on_post_save, A )
The idea here is that you wil be calling your endpoint after the transaction has been committed, so the instance involved in the transaction will be already saved ;).
I believe post_save fires after the save occurs, but before the transaction is commited to the database. By default, Django only commits changes to the database after the request has been completed.
Two possible solutions to your problem:
Manage your transactions manually, and fire a custom signal after you commit.
Have your second process wait a little while for the request to go through.
To be honest though, your whole setup seems a little bit nasty. You should probably look into Celery for asynchronous task queuing.
It's nice place to use decorators. There is slightly extended version of yoanis-gil's answer:
from django.db import transaction
from django.db.models.signals import post_save
def on_transaction_commit(func):
def inner(*args, **kwargs):
transaction.on_commit(lambda: func(*args, **kwargs))
return inner
#receiver(post_save, sender=A)
#on_transaction_commit
def trigger_on_post_save(sender, **kwargs):
# Do things here
Had same issue when creating new model from django admin. Overriding ModelAdmin.save_model method to manage transaction manually worked.
def save_model(self, request, obj, form, change):
from django.db import transaction
with transaction.commit_on_success():
super(ModelAdmin, self).save_model(request, obj, form, change)
# write your code here

Categories

Resources