Advice on django form change emailing - django

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

Related

Can I detect ManyToManyField mutations before they are applied?

I have a Django app that broadcasts changes of some of its models to its clients, to keep them up-to-date.
I have a separate app for that, that binds to the post_save signal of these models and triggers the broadcast.
Problems have come with models with ManyToManyFields. I am not very familiar with Django's handling of this but I understand that these fields are actually updated after the model is saved.
I was able to use the m2m_changed signal to react to the post_* actions and dispatch the broadcast after the object is all up-to-date. However, in the post_save handler, I still need to detect that the m2m field is going to be mutated, to avoid broadcasting incomplete data. How can I detect this , either in the post_save signal handler or the model's save method ? Is there a way to raise a flag on an object when a m2m field is about to be mutated ?
Here's what I've tried :
Handle the pre_* actions of the m2m_changed signal to detect incoming mutation of the field but that does not work, because the signal gets fired after post_save, which is too late (data has already been broadcasted).
Store initial values when the model instance is created, and compare them in the overriden save method to look for changes. This does not work because the fields are not changed and report to be == the initial value ; plus I read on other questions that this practice could cause race conditions.
Thanks a lot for the help.
I finally sorted this out. For reference:
TL;DR: The model layer is not the right place to do this ; the view layer is.
I re-implemented the notification system at the view level instead of doing it in the model layer. I made a mixin that adds notification features to my views.
Thanks to this solution :
I got rid of signals
I got much better control over what’s going on, which simplified the notification handler code quite a bit.

See if Django form submission came from the admin

I'm new to django, so I apologize if this has been asked. I'm using the post_save signal to run a task when a new object is created. I need to be able to check if the form was submitted from the admin page or if it was submitted on the live website, is this possible? Where might I find documentation on this?
post_save is too late in the process to log or take action on the source of the object.
post_save is a signal sent from the database/ORM, i.e. it is what is called after a save is done. Does the save function take any input about the source? No. Does that function put anything into the ORM or the database about the source? No.
You want to take whatever action it is in the view function where this occurs. Here is the simplest way to do it that I can think of. I shall assume that your preferred choice of action is to save where it was created in the database.
Consider the following:
class YourObject(models.Model):
name = models.CharField(max_length=30)
creation_location = models.CharField(max_length=30, default="Admin")
Here we have an object with a name and a creation_location, the purpose of the second being to tell where the object is created. To prevent any need to edit the Admin functionality, as that can be a pain, the default value is set to Admin.
Onto the view:
def create_model_view(request):
your_object = YourObject.objects.create(name='FirstObject', creation_location="View")
Here we have a view with a create function for the object. In the initialization, the default value of Admin for the creation_location is overwritten and set to View.

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.

Use a form inside a form or How to work with foreign keys in forms

I am going to use the documentation model as an example:
class Car(models.Model):
manufacturer = models.ForeignKey('Manufacturer')
# ...
class Manufacturer(models.Model):
# ...
Let's say I want to create a form to add a new Manufacturer, and in this form I want to be able to add new cars. How would it be done with django-forms?
Is it even possible?
Thank you in advance for your help!
The short answer:
You want modelformset_factory, documented here: http://docs.djangoproject.com/en/1.3/topics/forms/modelforms/#model-formsets
The still-rather-short answer, but with a couple of gotchas to watch for:
On the processing side, if you're creating both the Manufacturer and the multiple Car instances, you'll want to be sure to save the manufacturer first, before saving the individual cars (which must reference the manufacturer). Make sure this happens in a database transaction if you can help it.
One more note: if this is a bit confusing to you, beat in mind that there's no hard and fast rule saying that you can only process one form in a request. You just have multiple forms.Form (or subclasses thereof) objects within the HTML <form> tag, which posts to a single request location that processes each form individually and saves them out. Again, use a database transaction so that if something fails at the end, the entire thing gets rolled back and the user can correct their error without having bad or orphan data in the database.

Django - Image post processing on save

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.