Django - Image post processing on save - django

If I want to be able to "post process" an image after it is uploaded, crop it down to size and apply some compression. As it stands, I am doing this using the post_save signal, when the model is saved, I am accessing the file, applying the post production and saving over the original.
I am only doing this when the created argument of the post save signal is set to true to avoid unnecessary image processing every time the model is updated.
The problem
When the image field of an existing instance is updated, the post processing of the image is being skipped because the created flag is false.
How can I setup my model to only apply post processing to the image when the ImageField has changed, even if the model is already created? This app may not always be used with django admin, so overwriting the imagefield_save method isn't going to work.
Hope someone can help!

This question is from a long time ago, so probably not actual anymore?
did you have a look at:
pre_save.connect(before_mymodel_save, sender=MyModel)
have a look at the signal documentation of django
you create a function before_mymodel_save and you can try to do anything in there. If you're using a save inside the post or pre save functions: Just make sure you disconnect the signal if you save the MyModel object within this function (and connect again), to avoid endless loops.

Related

Copy/move linked file when changing model instance owner in Django admin

I have a very simple Image model
class Image(Model):
owner = ForeignKey(User)
img = ImageField(upload_to=image_file_path)
the image_file_path resolves to <username>/images/ where username is the Image model instance's owner.username
Now, when using Django admin to change the owner of the image, I want the physical image file to be moved to the appropriate path, i.e. <new_username>/images/.
What is the simplest / "correct" way of doing this?
Edit:
A few thoughts after experimenting a bit
post_save handlers: the idea is to make sure that the model is sane by moving the file into the correct place after the model changes. The problem is that if somthing bad happens – the file is missing / transfer errors out (e.g. if storage is on S3 and the connection is bad) / etc. – then you can end up in a loop trying to revert the change or end up with an inconsistent model / FS state
Django admin action: this seems to be a safer way because you can just copy the file, check that it's OK, then change the model, check that it's sane, then delete the old file. If anything breaks you can abort and since this is manually initiated from the admin interface there is no chance of users experiencing inconsisten behaviour.
pre_save: The same process as in 'Django admin action' could probably be used with pre_save, although if you abort due to problems then user experience suffers. On the other hand if copy / moving files is borked, users are not going to be able to upload anything anyway.
Is there a fourth way? Are there sanitizers for Models in Django?
Use the post_save signal of your Image model. In the same way you can use post_delete to actually delete the image file when the record is deleted.
You can review the Signals documentation here.

To use signals or override model save method?

Simple use case:
After a user updates a record, I want to get the changed fields and save them in a history table. I'm using django-ditryfields to grab this history. So my thought process was to use the pre_save signal to grab all the 'dirty' fields and them store them in my history table.
Problem there is that I can't get request.user while using signals. I need this to see which user has made the change to the record. My other thought was just to override the save method of my model but then I also can't get request.user from a model directly either. I would have to send a **kwarg['user'] with the user info from the view to get this info. This is fine but I am going to be making save calls from a bunch of different places around the code. I don't want to have to keep passing request.user every time I edit an object. This is why I'd love to have one spot, like a signal, to handle all of this. Perhaps some middleware I'm not familiar with?
Is there a better way to achieve such a thing?
You cannot access the user object from a signal.
You can consider using this third party package: django-requestprovider to access the request object in the signal.
The other way would be to overriding the models' save method.

Accessing M2M field elements during the execution of models's save method

I need to override django models's save method. I have used filter_horizontal for a many-to-many field in admin.py. I need to access the contents of that many-to-many field in the save method. But the many-to-many field is empty always when save method is executing. So I tried using Timer thread, to execute the process little later, but throws up error related to thread. Threads are not allowed in most of server-side technologies to avoid some deadlock problem. Is there any way that I can run a set of code that will execute immediately after save method has completed execution. I read something about signal.post_save() that is called in models's save_base method but I dont know whether that will be useful.
Looks like you might want to use a custom model form as here: http://reinout.vanrees.org/weblog/2011/11/29/many-to-many-field-save-method.html
There are a lot of links to related SO questions and bugs there an in the comments.

How to override/update information from POST when creating model

I have a view that handles a POST request and attempts to create a new object. However, I know that some of the POST'd data is invalid... But I want to fix it and go ahead and create the object.
The only way I can figure out to be able to 'fix' data in a ModelForm is to create a 'is_valid()' form. To do this, I can either create the form with the POST data, or I can create it with an already existing instance. Unfortunately, if I use the POST data, because some of it is invalid, the form won't validate and I am thus unable to get to the data in the form to fix it. If I create it with an already existing instance, this works, but when the form is displayed, any remaining errors are for whatever reason ignored (and thus don't show up on the web page.) I've tried a combination of creating the the Model form from the POST data and giving it an instance, but this doesn't seem to help. Additionally, I've tried modifying (a copy of) the POST data, fixing it, and then creating the ModelForm from the 'fixed' POST data. This sort of works, with the exception that I have some ImageFields in my form, and they seem to just be ignored.
Any help would be greatly appreciated. I have looked at every good page that I can find to no avail.
Perhaps there is a better way to do this? The problem I'm trying to solve is that I want to have a model that contains ImageFields. The first time I put up the form, the user needs to 'upload' images for each of the fields. However, if he doesn't update an image for one of the fields, I want the new form to come up with a Image upload button on the fields where images have not been uploaded, and just a text field with the image name for images that have been uploaded.
Edit 9/15/2010:
Ok, I think I can simplify all of the above question into this:
def testing( request ) :
test_form = UserProfileForm()
valid = test_form.is_valid()
return render( 'testing.tmpl', locals(), request )
When the above code is rendered, the 'valid' shows as False (as one might expect), but the 'test_form' renders without any errors. I've read through (if perhaps not understood?) the documentation on Models and ModelForms, and I see that most of the time a ModelForm (in my case: UserProfileForm) is created with a specified 'instance'. However, 1) I don't have an instance yet, 2) I would still expect the non-instance'd Form to display errors. I'm sure there is something I am missing. Please illuminate. :)
One more thing, which perhaps the answer to the above will answer anyway, but as far as I can tell, the is_valid() call is supposed to call the 'clean()' function I defined for the UserProfileForm. However, (not being a python guru) I placed 'raise ValidationError()' at the top of clean(), and when I run the code, no error is shown. Thoughts?
Update: I figured out the problem and the answer is below. Thanks!
You should have a look at how to clean form fields in django. You could either manipulate the data returned from the form there or make any kind of validation!
If your ImageFields are optional then you can still validate them (that they are otherwise correct).
Then it's a matter of adjusting your template to show either the uploaded file name or an file upload field depending on whether they've already uploaded one or not. Actually, it would probably be better to give them both fields in the first case. That's what the automatic admin does (the upload field is labeled "Change").
Well, after figuring out how to use the python debugger (pdb) and the fact that within emacs it kind of 'just works' (wow!?) I was able to find that my (empty) form was not bound. Googling bound forms pointed me to this page:
http://docs.djangoproject.com/en/dev/ref/forms/api/
RTFM'ing I find that I can pass an empty dictionary to my form and then everything starts to behave as I would expect. So, to summarize, there is a big difference between:
test_form = UserProfileForm()
and
test_form = UserProfileForm( {} )
The second version causes the rendering of the form to show all the errors (and to call 'clean()').
With risk of having this deleted by the moderator ;) Thank you to all those who commented and for your patience with a new django developer.

Advice on django form change emailing

Goal: On submission of a form to add/update/delete objects an email is sent out with the current contents of the DB object and the new contents of the DB object in html.
Example
Object Title was oldTitle and has been changed to newTitle
Object Date was oldDate and has been changed to newDate
My assumption this can be done two different ways. Directly through send_mail or via signals. My gut leans towards using signals to make sure I can grab previous content and the new content but I am not quite sure if this is the right way to go. Any advice you can give would be much appreciated. I couldn't find very much on this subject online.
Queue up the message to be sent in the pre_save signal, and send (or reap) them in the post_save signal.
Try overriding save() in your model objects, and grabbing the fields in question before calling super().
Here's a database email queue I wrote, which may help you with the actual sending of the mail:
http://gist.github.com/629663