Django signals doesn't work - python

My models.py:>
class Aval(models.Model):
cliente = models.ForeignKey(Cliente)
salao = models.ForeignKey(Salao)
rate = models.IntegerField(choices=RATE, default=5)
criacao = models.DateTimeField(blank=True, null=True, auto_now=True)
comentario = models.TextField(max_length=400, blank=True, null=True, default=None)
aprovado = models.BooleanField(default=False)
My signals.py:>
#receiver(post_save, sender=Aval)
def new_rate(sender, instance, created, **kwargs):
aval = instance
print("Aval is saved.")
I'm testing the signal post_save for Aval model, When I'm save some object Aval it not printing "Aval is saved" . What I'm doing wrong ?

Otiginal answer for Django < 1.7:
You should include:
import signals
to __init__.py file of your application.
Edit: Django >= 1.7:
Signals can be registered in django.apps.AppConfig.ready
as described in Signals documentation and AppConfig doc
Where should this code live?
Strictly speaking, signal handling and registration code can live
anywhere you like, although it’s recommended to avoid the
application’s root module and its models module to minimize
side-effects of importing code.
In practice, signal handlers are usually defined in a signals
submodule of the application they relate to. Signal receivers are
connected in the ready() method of your application configuration
class. If you’re using the receiver() decorator, import the signals
submodule inside ready().

I dont know if the paste is wrong, but in that code your model is named Avaliacao and not Aval, The Model and the sender argument has to match

Related

ImportError: cannot import name 'Flow' from partially initialized module 'firstapp.models.flows' (most likely due to a circular import), how to design

I have a problem, probably in my design.
In my Django project I have a 'Flow' object with 'event_reason' field in it, Foreign key to 'Event' object, therefore I need to import Event object, (otherwise I get the error: Cannot create form field for 'event_reason' yet, because its related model 'Event' has not been loaded yet). Therefore my code of my models is (in models/flows.py)
from .events import Event
class Flow(models.Model):
amount = models.DecimalField(max_digits=17, decimal_places=2, default=0.0)
flow_type = models.IntegerField(default=-1, choices=FLOW_CHOICES)
...
event_reason = models.ForeignKey('Event', on_delete=models.DO_NOTHING, default=None, related_name='events_flow',blank=True, null=True)
class FlowForm(ModelForm):
class Meta:
model = Flow
fields = '__all__'
In addition I have 'amount' and 'flow_type' fields in the 'Flow' objects and I want that the 'post_save' signal will save a new Flow associated to this Event. Therefore the Event model code is (in models/events.py):
from .flows import Flow
class Event(models.Model):
...
amount = models.IntegerField(default=0)
flow_type = models.IntegerField(default=-1, choices=FLOW_CHOICES)
#receiver(post_save, sender=Event)
def my_handler(sender, **kwargs):
new_flow = Flow.objects.create(...)
And I get the error ImportError: cannot import name 'Flow' from partially initialized module 'firstapp.models.flows' (most likely due to a circular import)
I understand that I cannot my a circular import but I want this functionality to work. How to solve this problem? Should I design the objects otherwise?
Thank you
The models.py should mostly only have model code, sometimes people do put signals (bad practice) but forms should absolutely not be written there.
One should write forms in forms.py so your should move that code to a separate file and import the model there. The forms etc. should not be imported in the models as their should be no need for them there.
So in forms.py:
from .models.flows import Flow
class FlowForm(ModelForm):
class Meta:
model = Flow
fields = '__all__'
# Other forms also go here
Now for signals it is best not to write them in models as I said before. They should be written in some file let's say signals.py and this will be imported in the apps config classes ready method.
So in signals.py:
# imports
#receiver(post_save, sender=Event)
def my_handler(sender, **kwargs):
new_flow = Flow.objects.create(...)
Now your app should have an app config by default in which you would add an import in the ready method. So in your apps apps.py just add an import in the ready method:
class YouAppConfig(AppConfig):
# some attributes here don't change
def ready(self):
from . import signals
Now remove those circular imports in your model files.

Admin side modification django

I have this class In which when I submit a name it goes to admin and only admin can approve this. I want that when admin approve a email automatically should be sent to user.
class myab(models.Model):
generic_name = models.CharField(max_length=50, null=False)
timestamp = models.DateTimeField(auto_now_add=True)
is_approved = models.BooleanField(null=False, default=False)
I only wanted to know how to trigger email code . I have everything else just wanted to understand how to trigger that function when Admin will approve this post.
You could make a listener function to the post-save signal and use a if to check if the instance was approved; read the signal docs for a better understanding.
The signal receiver could look similar to this:
from django.core import mail
from django.db.models.signals import post_save
from django.dispatch import receiver
from myapp.models import MyModel
#receiver(post_save, sender=MyModel)
def my_handler(sender, instance, created, *args, **kwargs):
...
if instance.is_approved:
mail.send_mail(...)
Highlighting this section of the docs:
Where should this code live?
[...] signal handling [...] code can live
anywhere you like, although it’s recommended to avoid the
application’s root module and its models module [...]
In practice, signal handlers are usually defined in a signals
submodule of the application they relate to [...]

How to add custom function to admin forms?

I would like to implement a function that updates quantity in LibraryBook each time the admin adds a book in SingleBook on the admin site. I have been searching for means to do so but to no avail. Any pointers including links to documentation would be very much appreciated.
Here is my code:
#models.py
class LibraryBook(models.Model):
book_title = models.CharField(max_length=100, blank=False)
book_author_id = models.ForeignKey(BookAuthors, on_delete=models.CASCADE)
category = models.ForeignKey(BookCategory, on_delete=models.CASCADE)
quantity = models.IntegerField(blank=False, default=0)
number_borrowed = models.IntegerField(default=0)
def __unicode__(self):
return unicode(self.book_title)
class SingleBook(models.Model):
serial_number = models.CharField(primary_key=True , max_length=150, blank=False)
book_id = models.ForeignKey(LibraryBook, on_delete=models.CASCADE)
is_available_returned = models.BooleanField(default=True)
is_borrowed = models.BooleanField(default=False)
def __unicode__(self):
return unicode(self.book_id)
#admin.py
class SingleBookAdmin(admin.ModelAdmin):
list_display = ('book_id', 'serial_number')
class LibraryBookAdmin(admin.ModelAdmin):
list_display = ('book_title', 'book_author_id', 'quantity')
search_fields = ('book_title', 'book_author_id')
fields = ('book_title', 'book_author_id', 'quantity')
PS: I have omitted the import and admin.site.register code
Django==1.9.8
django-material==0.8.0
django-model-utils==2.5.1
psycopg2==2.6.2
wheel==0.24.0
override save_model
If you only want to make the changes when an admin updates a record, the best way is to override the save_model method in ModelAdmin
The save_model method is given the HttpRequest, a model instance, a
ModelForm instance and a boolean value based on whether it is adding
or changing the object. Here you can do any pre- or post-save
operations.
class SingleBookAdmin(admin.ModelAdmin):
list_display = ('book_id', 'serial_number')
def save_model(self, request, obj, form, change):
admin.ModelAdmin.save_model(self, request, obj, form, change)
if obj.is_borrowed:
do something to obj.book_id.quantity
else:
do something to obj.book_id.quantity
post_save signal
from django.dispatch.dispatcher import receiver
from django.db.models.signals import post_save
#receiver(post_save, sender=SingleBook)
def user_updated(sender,instance, **kwargs):
''' Fired when a SingleBook is updated or saved
we will use the opporunity to change quantity'''
# your logic here
Other pointers
If on the other hand, you wanted to make changes based on all user actions, catching the post_save signal is the way to go. In either case, you might want to override the from_db method in the model to keep track of which fields have changed.
You might also want to change quantity and number_borrowed to IntegerFields (unless you are only using sqlite in which case it doesn't matter)
Also book_author_id should probably be book_author and book_id should probably be book (this is not a rule, just a convention to avoid the ugly book_id_id reference)
Use signals. Just attach post_save signal to SingleBook model and update according LibraryBook in it. post_save signal takes created argument, so you can determine if book is newly created or edited and apply your action based on that.
Also attach post_delete signal to decrease counter when SingleBook is removed.
To avoid race conditions (when 2 admins are adding books at the same time), I'm suggesting use of queryset update method together with F on changing LibraryBook counter, example:
LibraryBook.objects.filter(id=single_book.book_id_id).update(quantity=F('quantity') + 1)
Doing it that way will ensure that actual math operation will be performed on database level.

Django fixtures save with default value

I'm using Django 1.7 and I have a problem with my fixtures.
I would like Django to use the default value or use the save() method to create unspecified values.
Here are my current objects:
# File: uuidable.py
import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
class Uuidable(models.Model):
uuid = models.CharField(_('uuid'), blank=True,
null=False, unique=True,
max_length=64, default=uuid.uuid4()) # Tried here
class Meta:
abstract = True
def save(self, *args, **kwargs):
if self.pk is None:
self.uuid = uuid.uuid4() # Tried here also
super().save(*args, **kwargs)
# File: timestampable.py
from django.db import models
from django.utils.translation import ugettext_lazy as _
class Timestampable(models.Model):
created_at = models.DateTimeField(_('created at'), auto_now_add=True)
updated_at = models.DateTimeField(_('updated at'), auto_now=True)
class Meta:
abstract = True
# File: post.py
from project.lib.models.timestampable import Timestampable
from project.lib.models.uuidable import Uuidable
class Post(Timestampable, Uuidable):
title = models.CharField(_('title'), max_length=250, blank=False)
content = models.TextField(_('content'))
def __str__(self):
return self.title
As you can see, when I generate a new Post(), the created_at, updated_at and uuid values are automatically created on save(). But when I use fixtures, I get the following error:
[...]initial_data.yaml': Could not load post.Post(pk=None): UNIQUE constraint failed: post_post.uuid
If I specify a uuid in my fixture file, then I get an error on created_at and then on updated_at. So I have to specify the content of each field, even though I want it to be "automatic".
From the documentation (why is this in the django admin docs ?!), I know that the save() method is not called so this is why everything I put into the save() method doesn't work. But shouldn't the default or auto_now* features be enables/used ?
When fixture files are processed, the data is saved to the database as is. Model defined save() methods are not called, and any pre_save or post_save signals will be called with raw=True since the instance only contains attributes that are local to the model. You may, for example, want to disable handlers that access related fields that aren’t present during fixture loading and would otherwise raise an exception
Is there a way to "force" Django to automatically use the default or auto_now* features for fixtures ? I'm using manage.py syncdb to create all the tables etc.
I have searched on google and stack overflow but couldn't seem to find the right search keywords.
UPDATE-1: The following google group discussion says that objects are saved in raw mode, meaning that auto_now* features are not taken into account. I'm still searching to see if there is a way to hook some model function to the Django fixture saving.
The solution was to use django signals:
import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.db.models.signals import pre_save
from django.dispatch import receiver
class Uuidable(models.Model):
uuid = models.CharField(_('uuid'), blank=True,
null=False, unique=True,
max_length=64, default=uuid.uuid4())
class Meta:
abstract = True
#receiver(pre_save)
def set_uuid_on_save(sender, instance, *args, **kwargs):
if instance.pk is None:
instance.uuid = uuid.uuid4()
That way, the model/data is populated whatever way you create the model (via shell, fixtures, whatever).
Automatically loading initial data fixtures is deprecated in Django 1.7. One solution is via signals as you mentioned.
Another one that I prefer is to create a python script where you create all the needed data, and execute it in the shell:
python manage.py shell < create_initial_data.py
I think that the problem is when you put default=uuid.uuid4(). The parenthesis are too much, because they imply that you pass the result of uuid.uuid4() to default argument and not the function itself, so you should put default=uuid.uuid4.

Django post_save() signal implementation

I have a question about django.
I have ManyToMany Models here
class Product(models.Model):
name = models.CharField(max_length=255)
price = models.DecimalField(default=0.0, max_digits=9, decimal_places=2)
stock = models.IntegerField(default=0)
def __unicode__(self):
return self.name
class Cart(models.Model):
customer = models.ForeignKey(Customer)
products = models.ManyToManyField(Product, through='TransactionDetail')
t_date = models.DateField(default=datetime.now())
t_sum = models.FloatField(default=0.0)
def __unicode__(self):
return str(self.id)
class TransactionDetail(models.Model):
product = models.ForeignKey(Product)
cart = models.ForeignKey(Cart)
amount = models.IntegerField(default=0)
For 1 cart object created, I can insert as many as new TransactionDetail object (the product and amount). My question is. How can I implement the trigger? What I want is whenever a Transaction detail is created, I want the amount of the product's stock is substracted by the amount in the transactiondetail.
I've read about post_save() but I'm not sure how to implement it.
maybe something like this
when:
post_save(TransactionDetail,
Cart) #Cart object where TransactionDetail.cart= Cart.id
Cart.stock -= TransactionDetail.amount
If you really want to use signals to achieve this, here's briefly how,
from django.db.models.signals import post_save
from django.dispatch import receiver
class TransactionDetail(models.Model):
product = models.ForeignKey(Product)
# method for updating
#receiver(post_save, sender=TransactionDetail, dispatch_uid="update_stock_count")
def update_stock(sender, instance, **kwargs):
instance.product.stock -= instance.amount
instance.product.save()
Personally I would override the TransactionDetail's save() method and in there save the new TransactionDetail and then run
self.product.stock -= self.amount
self.product.save()
If you want to avoid getting maximum recursion depth exceeded, then you should disconnect signals, before saving within the signal handler. The example above (Kenny Shen's answer), would then be:
from django.db.models.signals import post_save
from django.dispatch import receiver
class TransactionDetail(models.Model):
# ... fields here
# method for updating
#receiver(post_save, sender=TransactionDetail, dispatch_uid="update_stock_count")
def update_stock(sender, instance, **kwargs):
instance.product.stock -= instance.amount
post_save.disconnect(update_stock, sender=TransactionDetail)
instance.product.save()
post_save.connect(update_stock, sender=TransactionDetail)
This is described thoroughly in Disconnect signals for models and reconnect in django, with a more abstract and useful example.
Also see: https://docs.djangoproject.com/en/2.0/topics/signals/#disconnecting-signals in the django docs.
If you really want to use signals in django please try this:
#import inbuilt user model
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=User)
def create_profile(sender, **kwargs):
# write you functionality
pass
then add default_app_config in the init file
default_app_config = "give your AppConfig path"
In fact, the docstrtring explains the Signals is in django.dispatch.Signal.connect:
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 referenceable.
Receivers must be able to accept keyword arguments.
If a receiver is connected with a dispatch_uid argument, it
will not be added if another receiver was already connected
with that dispatch_uid.
sender
The sender to which the receiver should respond. Must either be
a Python object, or None to receive events from any sender.
weak
Whether to use weak references to the receiver. By default, the
module will attempt to use weak references to the receiver
objects. If this parameter is false, then strong references will
be used.
dispatch_uid
An identifier used to uniquely identify a particular instance of
a receiver. This will usually be a string, though it may be
anything hashable.

Categories