Sitting over a day on it. Really can't understand why this signal is not triggered when a user is activated, no error log, no exception in the admin on activation. Can anybody help? The following code should result in a log message in the apache error.log when a user, right?
import logging
from django.dispatch import receiver
from registration.signals import user_activated
#receiver(user_activated)
def registered_callback(sender, **kwargs):
logger = logging.getLogger("user-activated")
logger.error("activated here")
same with user_registered
First of all im using django 1.8.3 .You should register your signal first. As far as i know, there are some methods to do that but this is what im doing;
Create signals.py in your app write your signal there;
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=your_model,dispatch_uid="yourmodel_save_receiver")
def post_save_yourmodel(sender, instance, **kwargs):
if instance.profile_status:
print "active"
else:
print "not active"
Then you should create apps.py. This file contains configuration information to your model.
from django.apps import AppConfig
class yourmodel_config(AppConfig):
name = 'yourmodel_config'
verbose_name = 'your_model config'
def ready(self):
import yourmodel.signals
With this whenever your app is ready, your signals will be imported
Finally open your __init__.py and add the following.
default_app_config = 'yourmodel.apps.yourmodel_config'
With this you are defining application configuration for your model.This example when ever yourmodel is saved, signal checks for profile_status attribute and prints output depending on the value(true or false) to your console. You can also add created parameter to your model to know that if instance of the model is created. created will return True if a new record was created. def post_save_yourmodel(sender, instance, created, **kwargs):. Otherwise this signal will be triggered whenever your model is saved with yourmodel.save().
Consider that is a post_save example.You can find list of the model signals from here.
Related
i m doing some project using django frame work i am a beginner and just used
django signals but i m confused that why do we need to imporrt signals file in app.py inside the ready function
code below makes question more clear i m stuck in this so require help
signal.py
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
#receiver(post_save,sender=User)
def create_profile(sender,instance,created,**kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save,sender=User)
def save_profile(sender,instance,**kwargs):
instance.profile.save()
app.py
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals
#i have no idea what this function does
what is the need of ready function here and why is it importing signals here???
what if i import signals at the top without using ready function??
what is the need of ready function here and why is it importing signals here?
The ready() method [Django-doc] is called after the registry is fully loaded. You thus can then perform some operations you want to perform before the server starts handling requests. This is specified in the documentation:
Subclasses can override this method to perform initialization tasks such as registering signals. It is called as soon as the registry is fully populated.
The reason the signals are imported here is because Django will not import the signals if you do not import those explicitly. If the signals module is not imported, then the signals are not registered on the corresponding models, and hence if you for example make changes to your User model, the signals will not be triggered.
Usually one adds a #noqa comment to the import line, to prevent a linter tool like pylint to raise warnings about an import that you do not use.
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals # noqa
I'm struggling with the following.
I'm trying to create a custom signal that will trigger when the current time will be equal to the value of my model's notify_on DateTimeField.
Something like this:
class Notification(models.Model):
...
notify_on = models.DateTimeField()
def send_email(*args, **kwargs):
# send email
signals.when_its_time.connect(send_email, sender=User)
After I've read through all docs and I found no information on how to implement such a signal.
Any ideas?
UPDATE:
Less naive approach with ability to discard irrelevant tasks: https://stackoverflow.com/a/55337663/9631956
Ok, thanks to comments by #SergeyPugach I've done the following:
Added a post_save signal that calls a function that adds a task to the celery. apply_async let's you pass eta - estimated time of arrival which can accept DateTimeField directly, that's very convenient.
# models.py
from django.db.models import signals
from django.db import models
from .tasks import send_notification
class Notification(models.Model):
...
notify_on = models.DateTimeField()
def notification_post_save(instance, *args, **kwargs):
send_notification.apply_async((instance,), eta=instance.notify_on)
signals.post_save.connect(notification_post_save, sender=Notification)
And the actual task in the tasks.py
import logging
from user_api.celery import app
from django.core.mail import send_mail
from django.template.loader import render_to_string
#app.task
def send_notification(self, instance):
try:
mail_subject = 'Your notification.'
message = render_to_string('notify.html', {
'title': instance.title,
'content': instance.content
})
send_mail(mail_subject, message, recipient_list=[instance.user.email], from_email=None)
except instance.DoesNotExist:
logging.warning("Notification does not exist anymore")
I will not get into details of setting up celery, there's plenty of information out there.
Now I will try to figure out how to update the task after it's notification instance was updated, but that's a completely different story.
In django's documentation there is two interesting signals that may help you on this task: pre_save and post_save.
It depends on your needs, but let's say you want to check if your model's notify_on is equal to the current date after saving your model (actually after calling the save() or create() method). If it's your case you can do:
from datetime import datetime
from django.contrib.auth.models import User
from django.db import models
from django.dispatch import receiver
from django.db.models.signals import post_save
class Notification(models.Model):
...
# Every notification is related to a user
# It depends on your model, but i guess you're doing something similar
user = models.ForeignKey(User, related_name='notify', on_delete=models.DO_NOTHING)
notify_on = models.DateTimeField()
...
def send_email(self, *args, **kwargs):
"""A model method to send email notification"""
...
#receiver(post_save, sender=User)
def create_notification(sender, instance, created, **kwargs):
# check if the user instance is created
if created:
obj = Notification.objects.create(user=instance, date=datetime.now().date())
if obj.notify_on == datetime.now().date():
obj.send_email()
And you should know, that django signals won't work by they own only if there is an action that triggers them. What this mean is that Django signals won't loop over your model's instances and perform an operation, but django signals will trigger when your application is performing an action on the model connected to a signal.
Bonus: To perform a loop over your instances and process an action regulary you may need an asyncworker with a Queue database (mostly, Celery with Redis or RabbitMQ).
I want to call a method after this object (in this example Question) is saved.
This is what I got:
class Question(models.Model):
...
def after_save(sender, instance, *args, **kwargs):
some_method(instance)
post_save.connect(Question.after_save, sender=Question)
It kind of works. But the problem is that the instance data is the old one (same as before the save). So some_method(instance) gets the old instance.
But I need the instance with the new data.
The method you call from post_save signal should be outside of the Model. You can put the method inside the models.py or in another file such as signals.py
class Question(models.Model):
...
def some_method(self):
return "hello"
def question_saved(sender, instance, *args, **kwargs):
instance.some_method()
post_save.connect(question_saved, sender=Question)
You can override save method, and call what ever you want after the object gets saved
Here is a related link: Django. Override save for model
What you want is something called signals: https://docs.djangoproject.com/en/3.2/topics/signals/.
Some operations send a signals before and after they're completed successfully. In the docs you can find a list of built-in signals. You can also create custom signals. For example assume that I want to do something specific each time a new user is created. Then my code would be something like this:
users/models.py
from django.contrib.auth.models import AbstractUser
from django.dispatch import receiver
from django.db.models.signals import post_save
class CustomUser(AbstractUser):
pass
###############
# Recieve the signals and get the signals that are sent
# after models are saved and whose sender is CustomUser
###############
#receiver(post_save, sender='CustomUser')
def do_something():
do_something_specific
You can also save your signals in a separate file called signals.py but you need to change your app's apps:
<appname>/apps.py
from django.apps import AppConfig
class AppnameConfig(AppConfig):
name = <appname>
def ready(self):
import <appname>.signals
I'm trying to setup a signal so that when a valid form is saved, a function is ran to carry out a related task.
My app structure is as follows;
- events
- helpers
- __init__.py
- status.py
- models
- signals
- __init__.py
- event.py
- __init__.py
- event.py
- status.py
- views
- __init__.py
- event.py
I believe signals need to be imported as early as possible, before models, so at the top of models/__init__.py I've got from .signals import *.
# views/event.py
class AddEventView(CreateView):
"""
View for adding an Event.
"""
model = Event
form_class = EventForm
success_url = reverse_lazy('events:all_events')
def form_valid(self, form):
self.object = form.save()
signals.event_status.send(
sender=None, request=self.request, event=self.object, status=None
) # Should the sender be self.object?
return super(AddEventView, self).form_valid(form)
# signals/event.py
from django.dispatch import Signal
event_status = Signal(providing_args=["request", "event", "status"])
# helpers/status.py
from ..models import Status, StatusHistory
from ..models.signals import event_status
def create_status(sender, **kwargs):
"""
Create a status for a given event.
"""
event = kwargs['event']
status = kwargs['status']
creator = User.objects.get(pk=event.creator)
try:
current_status = StatusHistory.objects.filter(
event=event).order_by('timestamp')[0]
except IndexError:
# Not sure what we're doing here yet.
pass
if not status:
status = Status.objects.get(description=_("Submitted"))
statushistory = StatusHistory.create(
event=event,
event_status=status,
user=creator
)
statushistory.save()
event_status.connect(create_status)
I'm running the debug server in Pycharm with a break point in the create_status() function & it's never getting hit.
Have I implemented this wrong?
I've used signals in some of my projects and I allways import the signals in the __init__.py of my Django APP (Same folder as settings.py, views.py, urls.py...)
__init__.py:
import signals
signals.py:
from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
from my_project.models import *
#receiver(post_save, sender=Modelname) # Called after an object is saved
def create_modelname(sender, **kwargs):
obj = kwargs['instance'] # I get the object being saved here
# ... Here I do whatever I want
#receiver(pre_delete, sender=Modelname) # Called before an object is deleted
def delete_modelname(sender, **kwargs):
obj = kwargs['instance']
# ... Do whatever you need
Remember this 2 imports:
from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
Remember to import the signals
To import the signals you need to add import signals in your __init__.py of your project
Using this code, this functions are called automatically by Django when an object of the class Modelname is created or deleted.
The receiver for created object is called after the object is created, and the receiver for deleted object is called before the object is deleted.
I think maybe you just need to import your helpers/status.py eg in models/__init__.py
otherwise your event_status signal gets defined ok but the signal handler create_status never gets connected by Django
if you only have one handler for that signal it might make sense to put it in the same module as the signal definition
I found one case that signal is not working.
Here are cases that signal(pre_save, post_save) won't happen.
Model.objects.filter(pk=pk).update(key=value)
Bulk model functions won't happen signals.
I am trying to create a project for creating feeds/activity feeds of a user with the help of a blog.
This is the signals.py:
from django.db.models import signals
from django.contrib.contenttypes.models import ContentType
from django.dispatch import dispatcher
from blogs.models import Blog
from picture.models import Photo
from models import StreamItem
def create_stream_item(sender, instance, signal, *args, **kwargs):
# Check to see if the object was just created for the first time
if 'created' in kwargs:
if kwargs['created']:
create = True
# Get the instance's content type
ctype = ContentType.object.get_for_model(instance)
if create:
si = StreamItem.objects.get_or_create(content_type=ctype, object_id=instance.id, pub_date = instance.pub_date)
# Send a signal on post_save for each of these models
for modelname in [Blog, Photo]:
dispatcher.connect(create_stream_item, signal=signals.post_save, sender=modelname)
When I try to run the server, it gives me an error:
AttributeError: 'module' object has no attribute 'connect'
Please help me out. Would be much appreciate. Thank you.
replace
from django.dispatch import dispatcher -> from django.dispatch import Signal
dispatcher.connect -> Signal.connect
dispatcher is module, interpreter tell it for you.