I want to delete all Generic Foreign Key relationships belonging to a Contact when said contact is deleted.
This is what I have tried so far:
#receiver(pre_delete, sender=Contact):
def contact_delete(sender, instance, **kwargs):
from unsubscribe.models import Unsubscribe
unsubscribe_list = Unsubscribe.objects.filter(object_id=instance)
for item in unsubscribe_list:
item.delete()
My issues are, how do I get the object_id of the instance. I only want to delete the related items of the object I'm deleting?
instance is the Contact object here. So, instance.id would give you the id of the contact object
from django.db.models.signals import pre_delete
from django.dispatch import receiver
#receiver(pre_delete, sender=Contact, dispatch_uid='<whatever>')
def contact_delete(sender, instance, using, **kwargs):
from unsubscribe.models import Unsubscribe
unsubscribe_list = Unsubscribe.objects.filter(object_id=instance.id, content_type__model='contact')
for item in unsubscribe_list: #This should be a single element in the queryset.
item.delete()
Related
My User has 2 objects.
1. admin - super admin
2. tom - user
MyModel with
class MyModel:
project_user = models.ManyToManyField(User)
Let's suppose tom is creating a MyModel object:
MyModel(project_user=request.user).save()
Here I would like to add admin user automatically to the project_user object when someone creates an object. What will the more efficient way to implement it,How about using signals or def save(self)?
I would recommend using Signals.
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_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
In the snippet above, I'm trying to create a custom model (i.e. Profile) when a default user once created.
Therefore, I think you could write like this:
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=MyModel)
def create_user(sender, instance, created, **kwargs):
if created:
User.objects.create_user(username=instance.username)
And create_user() is a helper function of Django.
Edit: Added what I import in the snippet.
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 have the following model:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User)
# ...
def __unicode__(self):
return u'%s %s' % (self.user.first_name, self.user.last_name)
When using the Django admin to delete the user, the profile gets deleted as well, which is what I want. However, when using the Django admin to delete the profile, the user does not get deleted, which is not what I want. How can I make it so that deleting the profile will also delete the user?
Since Profile links to User, it is the dependent model in the relationship. Therefore when you delete a user, it deletes all dependent models. However when you delete a profile, since User does not depend on profile, it is not removed.
Unfortunately, according to on_delete Django docs, there is no on_delete rule which deletes the parent relations. In order to do that, you can overwrite the Profile's delete method:
class Profile(models.Model):
# ...
def delete(self, *args, **kwargs):
self.user.delete()
return super(self.__class__, self).delete(*args, **kwargs)
Then when doing:
Profile.objects.get(...).delete()
will also delete the profile's user. However the delete method will not be called when deleting profiles using querysets (which is what is called in Django Admin) since then Django uses SQL DELETE to delete objects in bulk:
Profile.objects.filter(...).delete()
In that case, as recommended by Django docs, you will have to use post_delete signal (docs).
from django.dispatch import receiver
from django.db.models.signals import post_delete
#receiver(post_delete, sender=Profile)
def post_delete_user(sender, instance, *args, **kwargs):
if instance.user: # just in case user is not specified
instance.user.delete()
Use a signal on the Profile's delete method to go and delete the related User:
from django.db.models.signals import post_delete
def delete_related_user(sender, **kwargs):
deleted_profile = kwargs['instance']
deleted_profile.user.delete()
post_delete.connect(delete_related_user, sender=Profile)
I want to execute some code in a Django model when it is first created. After that whenever it is saved I want to execute some other code. The second task can be easily done by overriding the save() method.
How can I do the first task?
Extending sdolan's answer by using receiver decorator:
from django.db import models
from django.dispatch import receiver
class MyModel(models.Model):
pass
#receiver(models.signals.post_save, sender=MyModel)
def execute_after_save(sender, instance, created, *args, **kwargs):
if created:
# code
You can use django signals' post_save:
# models.py
from django.db.models import signals
class MyModel(models.Model):
pass
def my_model_post_save(sender, instance, created, *args, **kwargs):
"""Argument explanation:
sender - The model class. (MyModel)
instance - The actual instance being saved.
created - Boolean; True if a new record was created.
*args, **kwargs - Capture the unneeded `raw` and `using`(1.3) arguments.
"""
if created:
# your code goes here
# django 1.3+
from django.dispatch import dispatcher
dispatcher.connect(my_model_post_save, signal=signals.post_save, sender=MyModel)
# django <1.3
from django.db.models.signals import post_save
post_save.connect(my_model_post_save, sender=MyModel)
field.default
The default value for the field. This can be a value or a callable object. If callable it will be called every time a new object is created.
So, we can use any field callable to do what we want on creation, huh? ;-)
Hay, I'm using django's contrib.auth system which obviously allows me to create User objects, I'm also using the profile module. This is loaded through AUTH_PROFILE_MODULE.
Using signals how would i got about create a new UserProfile object when a User is created?
Thanks
I'm creating a new entry in Account which acts as my UserProfile:
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from wizard.models import Account
def make_account(sender, **kwargs):
if 'created' not in kwargs or not kwargs['created']:
return
user = kwargs["instance"]
account = Account(user=user, name="Account for %s" % user.username)
account.save()
post_save.connect(make_account, sender=User, weak=False)