Update one of the field in models after POST save - python

I want to update one field of my model after post save. For that I am using post_save signal but when I try to save the model it always get trapped in some kind of infinite loop and in the end I am getting max, recursion depth error
My code is as follows :
class UserProfile(models.Model):
.
.
.
def profile_thumbanil(sender, created, instance , **kwargs):
profile = UserProfile.objects.get(id = instance.id)
thumb = handlers.create_thumbanil(profile.image, profile.user_id)
profile.thumbnail_image = thumb
profile.save()
post_save.connect(profile_thumbanil, sender=UserProfile)
I don't know what's the error here. If anyone can tell me another way of saving the data after post_save then that will be also fine.
Thanks
Edit :
save() will not work in my case because I am creating the thumbnail of images and the script which I using resize the images which are already exist on server and thus untill the save() finished its work image will not be saved on server and thus I cannot resize it that's why I can only run my function after save() finished its work so that image will be saved on server and I can resize it.
I can use Update() when user try to save images via UI and in that case my function works because Image is already saved into db but when admin (django-admin) try to upload an image then issue comes.
So I need to call my function in such a way that whenever django admin save/edit profile images I can call my function but as I said my function only works after actual save() finished its work.

You can redefine save method of the model. It is more appropriate in your case than using signals because you modify the same instance.
Maybe this would be helpful:
http://www.martin-geber.com/thought/2007/10/29/django-signals-vs-custom-save-method/

You can get the object using filter and then use the update method to save the corresponding field.
def profile_thumbanil(sender, created, instance , update_fields=["thumbnail_image"], **kwargs):
profile = UserProfile.objects.get(id = instance.id)
thumb = handlers.create_thumbanil(profile.image, profile.user_id)
profile.update(thumbnail_image = thumb)
post_save.connect(profile_thumbanil, sender=UserProfile)
An alternative method is to disconnect the post save signal, save relevant fields then reconnect the post save method.

Related

Django - Connect to post_init signal on model change form only

I would like to use a post_init signal connection on a model instance to call a remote API which will fetch some data and then use this as a sort of dynamic model property that gets assigned to that instance when the change form is loaded.
Specifically, I have another system that uses a completely different framework that has a model that is (mostly) the same as a model I have in Django. The post_init signal should check the remote version to see if there are any images saved to it, and then assign this boolean as a dynamic model property to the Django model instance.
This would give me a condition to check during the model's post_save signal that I can use to determine if I need to upload images to the remote model or not.
Since this will be calling a remote system, I don't want the API call in post_init to be called for every single model instance when loading the admin model list page, I only want it to happen on the change form.
How can I exclude this expensive API call from all post_init signals except for when it is triggered from the model instance's admin change form?
If i understood you correct, you need to make the API call when post_save is emitted. you have the option to check if the instance is created or updated.
#receiver(post_save, sender=Your Model)
def upload_image(sender, instance, created, **kwargs):
try:
#This will check if the instance is created or updated
if created:
#upload the image or do any api calls
#update your dynamic field on the instance
except Exception as e:
print(e)
This way you will be making only one API call after creation. If you want it to happen only during updates, just change the condition to
if not created :

Validate model instance from python using Django admin validators

I'm working on the importing script that saves data from CSV to Django database. Saving process looks like this:
instance = ModelName(**kwargs)
instance.save()
# No errors reported
But when I try to edit and save some items using admin panel it shows me a message that some of the field values (like URL fields and custom validators) is not valid.
Question: Is there any way to validate model instance from the python code using Django admin validators?
The issue is save() does not validate by default.
To address this, you can call the model's full_clean method to validate before calling save.
So, under the hood,
This method calls Model.clean_fields(), Model.clean(), and
Model.validate_unique() (if validate_unique is True), in that order
and raises a ValidationError that has a message_dict attribute
containing errors from all three stages.
The code would look something like this:
instance = ModelName(**kwargs)
instance.full_clean() #Does validation here
instance.save()

django assign value to positiveIntegerField programmatically

I am working on a little django application where the model contains a TextField and a PositiveIntegerField.
I need the PositiveInegerField to be populated with the number of words in the TextField.
This could be done via custom javascript code that count the number of words in the TextField's text area and put the value to the word count field text box before submitting the form but I do not want to play with custom javascript in the admin.
How to assign a value to a PositiveIntegerField programmatically?
This can be achieved using a pre_save Signal.
Create a signal-function like this:
def calculate_wordcount(sender, instance, **kwargs):
# count the words...
instance.word_count = # your count from line above
Then attach this function to your model. The preferred way since Django 1.7 is the application configuration (see doc).
You can attach your function in the AppConfig ready() - method
def ready(self):
pre_save.connect(calculate_wordcount,
sender= ,# your model
weak=False,
dispatch_uid='models.your_model.wordcount')
I'll leave all necessary imports up to you, please comment, if you need further direction!
While in general I think save signals are a reasonable idea, in this particular case you can also override .save() yourself
class MyClass(models.Model):
[stuff]
def save(self, *args, **kwargs):
self.wordcount = #something that calculates wordcount
super(self.myClass, self).save(*args, **kwargs) #Call django's save!
(as a rule of thumb, I'll overwrite save() if I'm just doing things to one model, but use a signal if I'm using one model to affect another. The idea is to keep all things affecting a model near it in the code. But this gets into personal philosophy)
Also WARNING: No matter which version you use, the signal or overwriting save(), bulk_create and bulk_delete will not send a signal or call your specific save function. See the documentation here: https://docs.djangoproject.com/en/1.8/topics/db/models/#overriding-predefined-model-methods

Call a function after saving a model

I'm working on a Django application connected to a LDAP server. Here's the trick i'm trying to do.
I have a model called system containing some information about computers. When i add a new system, the model generates a unique UUID, like and AutoField. The point is that this parameter is generated when saving, and only the first time.
After saved, i need a function to keep that UUID and create a new object on my LDAP.
Since i didn't know a lot about signals, i tried overriding the model save function in this way:
def save(self):
# import needed modules
import ldap
import ldap.modlist as modlist
[--OPERATIONS ON THE LDAP--]
super(System, self).save()
In this way, if i modify an existing system all work as should, because its UUID has been generated already. But if i try adding a new system i find the error UUID is None, and i can't work with an empty variable on LDAP (also it would be useless, don't u think so?)
It seems i need to call the function that work on LDAP after the system is saved, and so after an UUID has been generated. I tried to unserstand how to create a post_save function but i couldn't get it.
How can i do that?
Thanks
As you stated on your own, you do need signals, it will allow your code to stay more clean and seperate logic between parts.
The usual approach is to place signals just at the end of your models file:
# Signals
from django.dispatch import receiver
#receiver(models.signals.post_save, sender=YourModel)
def do_something(sender, instance, created, **kwargs):
....
On the above example we connect the post_save signal with the do_something function, this is performed through the decorator #receiver, sender of the decorator points to your Model Class.
Inside your function you have instance which holds the current instance of the model and the created flag which allows you to determine if this is a new record or an old (if the Model is being updated).
Signals would be excellent for something like this, but moving the line super(System, self).save() to the top of the save method might work as well. That means you first save the instance, before passing the saved object to the LDAP.

How to use Post_save in Django

I am trying to add points to a User's profile after they submit a comment- using the Django comment framework. I think I need to use a post_save but am not sure to be perfectly honest.
Here is what I have as a method in my models.py:
def add_points(request, Comment):
if Comment.post_save():
request.user.get_profile().points += 2
request.user.get_profile().save()
From the examples of post_save I've found, this is far from what is shown - so I think I am way off the mark.
Thank you for your help.
Unfortunately this makes no sense at all.
Firstly, this can't be a method, as it doesn't have self as the first parameter.
Secondly, it seems to be taking the class, not an instance. You can't save the class itself, only an instance of it.
Thirdly, post_save is not a method of the model (unless you've defined one yourself). It's a signal, and you don't call a signal, you attach a signal handler to it and do logic there. You can't return data from a signal to a method, either.
And finally, the profile instance that you add 2 to will not necessarily be the same as the one you save in the second line, because Django model instances don't have identity. Get it once and put it into a variable, then save that.
The Comments framework defines its own signals that you can use instead of the generic post_save. So, what you actually need is to register a signal handler on comment_was_posted. Inside that handler, you'll need to get the user's profile, and update that.
def comment_handler(sender, comment, request, **kwargs):
profile = request.user.get_profile()
profile.points += 2
profile.save()
from django.contrib.comments.signals import comment_was_posted
comment_was_posted.connect(comment_handler, sender=Comment)

Categories

Resources