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 :
Related
Say for example I have a Model called Player. I want Model objects of Player created when new Users are added in django (from django.contrib.auth.models.User) such that each User object in User model has its own object in Player model. I know that I would technically have to use models.ForeignKey() to create a field in Player model and relate it to the User model and that I don't have to worry about deletion of users if I use the on_delete=models.CASCADE parameter in models.ForeignKey() but how do I automatically create these objects with like default values and such.
Initially this was my code to do so:
for name in User.objects.all().values_list('username', flat=True):
if name not in Player.objects.all().values_list('player_name', flat=True):
new_player = Player(player_name=name, current_level_no=None, no_of_moves=None)
new_player.save()
else:
pass
But this would give me a DatabaseIntegrityError that "Models aren't loaded yet". So I am confused about what to do or how to go forward.
As always, I greatly appreciate all answers!
Check out Django signals, specifically the post_save signal. Essentially, Django lets you define a function that will be run whenever your User model is saved. You'd use it something like this:
from django.db.models.signals import post_save
def create_player(sender, instance, created, **kwargs):
if created:
Player.objects.create(user=instance) # instance is the User object
post_save.connect(create_player, sender=User)
Note that if you already have User objects in your database, this won't create Player objects for them; you'll want to do that by running a loop similar to the one you posted in the Django shell (using python manage.py shell). The signal will run on every subsequent save of a user model.
Is there any way to pass additional parameters to instance I'm saving in DB to later access them after the instance is saved?
Brief example of my case:
I'm using Django's signals as triggers to events, like sending a confirmation email, executed by other processes, like workers.
I'm willing to specify which instance and when should trigger the event, and which should not: sometimes I want created/updated records to trigger series of events, and sometimes I want them to be processes silently or do some other actions.
One solution for this is saving desired behaviour for specific instance in model's field like JSONField and recover this behaviour at post_save, but this seems very ugly way of handlign such problem.
I'm using post_save signal as verification that instance was correctly saved in the DB, because I don't want to trigger event and a moment later something goes wrong while saving instance in DB.
Instances are saved through Django Forms, backend routines and RestFramework Seralizers
One solution is to use an arbitrary model instance attribute (not field) to store the desired state. For example:
def my_view(request):
...
instance._send_message = True if ... else False
instance.save()
#receiver(post_save, sender=MyModel)
def my_handler(sender, instance, **kwargs):
if instance._send_message:
...
I am attempting to combine Flask-Admin and a Flask S3 Tool Suite written by someone to store images to Amazon S3 simple storage. The tool I am trying to specifically use is the S3Saver functionality and in the article link the user goes on to say that to incorporate this tool, I would ideally use it on a flask-admin callback.
However, I cannot find a list of Flask-Admin callbacks anywhere. I have done my homework and I have literally checked the entire docs and source code for these callbacks Flask-Admin documentation .
In flask_admin.contrib.sqla I found some methods that kind of resembled a callback but its not what I am looking for. I imagine that it is something close to Rails's before_action and after_action. Can someone please point me in the right direction?
By the way this is the actual quote from the article
If you wanted to handle deleting files in the admin as well, you could (for example) use s3-saver, and hook it in to one of the Flask-Admin event callbacks
What callbacks are those?
Flask-Admin Callback Functions (as far as I know)
after_model_change(form, model, is_created)
#Called from create_model after successful database commit.
after_model_delete(model)
#Called from delete_model after successful database commit (if it has any meaning for a store backend).
on_form_prefill(form,id)
# Called from edit_view, if the current action is rendering the form rather than receiving client side input, after default pre-filling has been performed.
on_model_change(form,model,is_created)
#Called from create_model and update_model in the same transaction (if it has any meaning for a store backend).
on_model_delete(model)
# Called from delete_model in the same transaction (if it has any meaning for a store backend).
More info at http://flask-admin.readthedocs.org/en/latest/api/mod_model/ (search for "Called from")
Here is the documentation for Flask Admin events :
https://flask-admin.readthedocs.org/en/latest/api/mod_model/#flask_admin.model.BaseModelView.on_form_prefill
on_form_prefill(form, id) Perform additional actions to pre-fill the edit form.
Called from edit_view, if the current action is rendering the form
rather than receiving client side input, after default pre-filling has
been performed.
By default does nothing.
You only need to override this if you have added custom fields that
depend on the database contents in a way that Flask-admin can’t figure
out by itself. Fields that were added by name of a normal column or
relationship should work out of the box.
Parameters: form – Form instance id – id of the object that is going
to be edited
on_model_change(form, model, is_created) Perform some actions before a model is created or updated.
Called from create_model and update_model in the same transaction (if
it has any meaning for a store backend).
By default does nothing.
Parameters: form – Form used to create/update model model – Model
that will be created/updated is_created – Will be set to True if model
was created and to False if edited
on_model_delete(model) Perform some actions before a model is deleted.
Called from delete_model in the same transaction (if it has any
meaning for a store backend).
By default do nothing.
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.
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.