django post_save call from within sending Model? - python

I have a pretty simple model that works:
class Badge(models.Model):
name = models.CharField(max_length=16, help_text="Name for Badge")
category = models.ForeignKey(BadgeCategory, help_text="Category for badge")
description = models.CharField(max_length=32, help_text="A brief description")
file = models.ImageField(upload_to=format_badge_name)
signals.post_save.connect(create_badge, sender=Badge)
I know my create_badge function in signals.py works. If I send it without a value for sender, it says the sender is a LogEntry object. I want/need to reference some of the instance information in the post_save script like below:
def create_badge(sender, instance, created, **kwargs):
from userinfuser.ui_api import UserInfuser
from django.conf import settings
if created:
api_key = settings.API_KEY
api_email = settings.API_EMAIL
ui = UserInfuser(api_email, api_key)
ui.create_badge(instance.category.name, instance.name, instance.description, instance.file.url)
Where can I call my post_save call so it's aware of Badge (I'm assuming this is the fix?
Thanks.

Just connect the signal with sender=Badge after Badge is defined, tested example:
from django.db import models
from django.db.models import signals
def create_badge(sender, instance, created, **kwargs):
print "Post save emited for", instance
class BadgeCategory(models.Model):
name = models.CharField(max_length=100)
class Badge(models.Model):
name = models.CharField(max_length=16, help_text="Name for Badge")
category = models.ForeignKey(BadgeCategory, help_text="Category for badge")
description = models.CharField(max_length=32, help_text="A brief description")
signals.post_save.connect(create_badge, sender=Badge)
Test shell session:
In [1]: category = BadgeCategory(name='foo')
In [2]: category.save()
In [3]: badge = Badge(category=category, name='bar', description='test badge')
In [4]: badge.save()
Post save emited for Badge object

Related

How to run function if model instance field is equal to inplay (Django)

I have a model that's being updated by a background task every few seconds.
I would like to execute a function when the instance of the attribute status changes to inplay
I have looked through documentation and examples but can a't find what I'm looking for. Would signals be the best option to call a function after model instance field changes to
inplay'?
from django.db import models
class testModel(models.Model):
player1 = models.CharField(null=True, max_length=50)
player2 = models.CharField(null=True, max_length=50)
Player1_odds = models.FloatField(null=True)
Player2_odds = models.FloatField(null=True)
status = models.CharField(null=True, max_length=10)
complete = models.CharField(null=True, max_length=10)
from django.dispatch import receiver
from django.db.models.signals import pre_save, pre_delete, post_save,
post_delete
from django.dispatch import receiver
#receiver(post_save, sender=testModel)
def post_save(sender, instance, created, **kwargs):
# if status is = inplay call send
#
#
pass
def send()
# run bet
You should choice overriding save method rather than signals because your changes are specific to testModel only. So this is how you would override save method:
class testModel(models.Model):
status = models.CharField(null=True, max_length=10)
# your other model fields
def save(self):
super(testModel, self).save() # this will save model
if self.status == 'inplay':# this will check if the status is "inplay" after change
send()
Yes you can use signals for that. In your case you can get the update status from the instance.
#receiver(post_save, sender=testModel)
def post_save(sender, instance, created, **kwargs):
if instance.status == 'inplay':
send()

Django models.py OneToOneField --- help creating a relationship between two models

I am having trouble testing a relationship between two models (CustomUser and Profile) located in different apps. I'm hoping someone can identify where I am going wrong here:
Here is my profiles/models.py --- you can see my user field attempting to create a OneToOne with with my users/models.py:
from django.db import models
from core.models import TimeStampedModel
class Profile(TimeStampedModel):
user = models.OneToOneField('users.CustomUser', on_delete=models.CASCADE)
first_name = models.CharField(max_length=30, blank=True)
last_name = models.CharField(max_length=30, blank=True)
bio = models.TextField(blank=True)
image = models.URLField(blank=True)
def __str__(self):
return self.user.username
Here is my users/models.py:
class CustomUser(AbstractBaseUser, PermissionsMixin, TimeStampedModel):
username = models.CharField(db_index=True, max_length=255, unique=True)
email = models.EmailField(db_index=True, unique=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_provider = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = CustomUserManager()
def __str__(self):
return self.email
#property
def token(self):
return self._generate_jwt_token()
def get_short_name(self):
return self.username
def _generate_jwt_token(self):
dt = datetime.now() + timedelta(days=60)
token = jwt.encode({
'id': self.pk,
'exp': int(dt.strftime('%s'))
}, settings.SECRET_KEY, algorithm='HS256')
return token.decode('utf-8')
So the idea is that when I create a new user, a profile is automatically created as well. To do this, I am using a post_save signal in my users app:
users/signals.py:
from django.db.models.signals import post_save
from django.dispatch import receiver
from conduit.apps.profiles.models import Profile
from .models import User
#receiver(post_save, sender=User)
def create_related_profile(sender, instance, created, *args, **kwargs):
if instance and created:
instance.profile = Profile.objects.create(user=instance)
And finally an update to my users/init.py file:
from django.apps import AppConfig
class UsersAppConfig(AppConfig):
name = 'django.users'
label = 'users'
verbose_name = 'Users'
def ready(self):
import users.signals
default_app_config = 'django.users.UsersAppConfig'
That last update is something I am relatively unfamiliar with. I suspect this is where my problem is located.
I am able to resister a new user via an api call with no problem, however, when I test to see if a Profile object exists for that new user, I am left with the following error:
python manage.py shell
from users.models import CustomUser
u = CustomerUser.objects.last()
u
<CustomUser:testuser#gmail.com> --- everything works to this point
u.profile --- this is where it breaks down
I'm left with this error in shell:
users.models.CustomUser.profile.RelatedObjectDoesNotExist: CustomUser has no profile.
Any help would be appreciated, thanks!
I think your error is in your signal method:
from django.db.models.signals import post_save
from django.dispatch import receiver
from conduit.apps.profiles.models import Profile
from .models import User # you have CustomUser but you are calling User
#receiver(post_save, sender=User)
def create_related_profile(sender, instance, created, *args, **kwargs):
if instance and created: # you should only have created because you want this happen only when it is created
instance.profile = Profile.objects.create(user=instance)
Also, I see no need updating the users init.py.

Django: How to update the field of another model during object creation

I have a simple chat app.
class Thread(models.Model):
participants = models.ManyToManyField(User)
last_message_time = models.DateTimeField(null=True, blank=True)
class NewMessage(models.Model):
message = models.CharField(max_length=500)
sender = models.ForeignKey(User)
thread = models.ForeignKey(Thread, related_name = 'thread_to_message')
datetime = models.DateTimeField(auto_now_add=True)
Every time a a NewMessage object is created, I would like to update the last_message_time in the Thread model with the datetime from the NewMessage object that was just created. How can I go about doing this?
The simplest way is probably with a post_save signal handler for NewMessage.
from django.db.models.signals import post_save
def update_thread(sender, **kwargs):
instance = kwargs['instance']
created = kwargs['created']
raw = kwargs['raw']
if created and not raw:
instance.thread.last_message_time = instance.datetime
instance.thread.save()
post_save.connect(update_thread, sender=NewMessage)
You could also use a custom save method on NewMessage.

Preventing duplicate signals for post_save in Django 1.4.3

I'm trying to write some code that sends an email every time one of the users modifies a model object. Currently, I'm working on having the one of the methods in models.py receive a post_save signal. I realize it's a well known fact that the post_save signal is usually sent twice, thus, the workaround is to utilize the dispatch_uid parameter. I have done this, but for some strange reason, I continue to receive two signals. Here's the code in my app's model.py file.
from django.db import models
from django.db.models.signals import post_save
def send_email(sender, **kwargs):
print "Signal sent." #just a placeholder
post_save.connect(send_email, dispatch_uid="unique_identifier")
class Library_Associates (models.Model):
first_name = models.CharField(max_length = 200)
last_name = models.CharField(max_length = 200)
department_choices = (
('ENG', 'Engineering'),
('ART', 'Arts and Sciences'),
('AFM', 'Accounting and Financial Managment'),
('MAT', 'Mathematics'),
)
department = models.CharField(max_length = 3, choices = department_choices, default = 'ENG')
pub_date = models.DateTimeField ('date published')
def __unicode__(self):
return self.first_name
class Meta:
verbose_name_plural = 'Library Associates'
class Info_Desk_Staff (models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
salary = models.IntegerField()
hours_worked = models.IntegerField()
def __unicode__(self):
return self.first_name
class Meta:
verbose_name_plural = 'Info Desk Staff'
I already restarted the server several times, reset/deleted all the data for the app and I continue to still receive two signals. Is there something inherently wrong with my code? Any suggestions or insight would be greatly appreciated! Thanks!
Your problem comes from the fact that each time you modify an object via the admin interface, admin app creates the django.contrib.admin.models.LogEntry instance that represents changes made.
Because you are listening to post_save on all objects, your listener is called twice - once for your model, and the second time for the LogEntry model.
List of possible solutions includes:
Registering your listener separately for each of your models (e.g. select your models somehow and do it in a loop) using the sender argument in the post_save method.
for model in get_models():
post_save.connect(send_email, sender = model, dispatch_uid='unique_identifier')
Check if the sender sent to the listener is not an instance of django.contrib.admin.models.LogEntry
from django.contrib.admin.models import LogEntry
...
def send_email(sender, **kwargs):
if isinstance(sender, LogEntry):
return
Give your models a common super class and use that for testing in the listener
class MyModel(models.Model):
pass
class Library_Associates (MyModel):
...
class Info_Desk_Staff (MyModel):
...
def send_email(sender, **kwargs):
if not isinstance(sender, MyModel):
return

django auto filling some data based on model attribute

am working on a concept in which I want to capture certain information when a model is saved. To understand the full picture, I have a app core with the following model
core/models.py
from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from transmeta import TransMeta
from django.utils.translation import ugettext_lazy as _
import signals
class Audit(models.Model):
## TODO: Document
# Polymorphic model using generic relation through DJANGO content type
operation = models.CharField(_('Operation'), max_length=40)
operation_at = models.DateTimeField(_('Operation At'), auto_now_add=True)
operation_by = models.ForeignKey(User, null=True, blank=True, related_name="%(app_label)s_%(class)s_y+")
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
Audit model is a generic content type, am currently attaching it with other apps such as in blog
blog/models.py
from django.db import models
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
from django.template.defaultfilters import slugify
from django.utils.translation import ugettext_lazy as _
# Create your models here.
class Meta:
verbose_name_plural = "Categories"
class article(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(editable=False, unique_for_year=True)
content = models.TextField()
is_active = models.BooleanField()
published_at = models.DateTimeField('Publish at',auto_now=True)
related_articles = models.ManyToManyField('self', null=True, blank=True)
audit_obj = generic.GenericRelation('core.Audit', editable=False, null=True, blank=True)
My first attempt was, I made a post_save signal in which I was checking if the instance passed containing audit_obj attribute and then saving a record in using article.audit_obj.create().save().
Unfortunately, this did not entirely work out for me since I cannot pass the request nor I can access the request to retrieve the user information.
So, I was thinking to create a custom signal and override the form_save method (if there is such a thing) and then using arguments to pass the request object as well as the model object.
Any advice on how I can do that?
Regards,
EDIT (20th of Jan, 2011):
Thanks #Yuji for your time. Well, what am trying to achieve is to keep my code as DRY as possible. What I want to do ultimately, every time I create new model, I will only create an additional attribute and name it audit_obj and I will create a single piece of code, either a signal or to override the save method inside the django core itself. The peiece of code will always check if an attribute with the following name exists and therefore creates a record in aduti table.
I'd just create a function in my model class or Manager and call it from my form save (wherever yours might be)
class AuditManager(models.Manager):
def save_from_object(self, request, obj):
audit = Audit()
audit.object_id = obj.id
audit.operation_by = request.user
# ...
audit.save()
class Audit(models.Model):
## TODO: Document
# Polymorphic model using generic relation through DJANGO content type
operation = models.CharField(_('Operation'), max_length=40)
operation_at = models.DateTimeField(_('Operation At'), auto_now_add=True)
operation_by = models.ForeignKey(User, null=True, blank=True, related_name="%(app_label)s_%(class)s_y+")
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
objects = AuditManager()
class MyBlogForm(forms.ModelForm):
class Meta:
model = article # btw I'd use Capital Letters For Classes
def save(self, *args, **kwargs):
super(MyBlogForm, self).save(*args, **kwargs)
Audit.objects.save_from_object(request, self.instance)

Categories

Resources