To use signals or override model save method? - django

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.

Related

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.

Django admin, Proper way to create change_form that don't really save objects

Let's say I have a model Message. In admin, I need to add a message by submitting/uploading a message file, not by posting message fields as normally would. The process is, I get the file uploaded, send it to a processing program which parse the file, do some further processing and then add an entry into database.
So I create a custom form for ModelAdmin(assign form = CustomForm) with one FileField and override save_model() to not to save anything. However, this isn't a proper way as the doc clearly mention ModelAdmin's save_model() method is not for veto purpose. So what would be the proper way for this?
Some suggest an extra button pointed to a custom view, while it's surelly possible but logically I'm still adding a message so an extra view doesn't seem nature.

Using QuerySet.update() versus ModelInstance.save() in Django

I am curious what others think about this problem...
I have been going back and forth in the past few days about using QuerySet.update() versus ModelInstance.save(). Obviously if there are lots of fields being changed, I'd use save(), but for updating a couple of fields, I think it's better to use QuerySet.update(). The benefit of using QuerySet.update() is that you can have multiple threads running update() at the same time, on different fields of the same object, and you won't have race issues. The default save() method saves all the fields, so parallel save() from two threads will be problematic.
So then the issue is what if you have overloaded, custom save() methods. The best I can think of is to abstract whatever in the custom save() method into separate updater methods that actually uses QuerySet.update() to set a couple of fields in the model. Has anyone used this pattern?
What's a bit irritating is that in Django Admin, even in editing in change list mode where you are editing just one field, the entire model is saved. This basically means if someone have a change list open on his/her browser, while some where else in the system a field gets updated, that updated value will be thrown away when this user saves changes from the change list. Is there a solution to this problem?
Thoughts?
Thanks.
The main reason for using QuerySet.update() is that you can update more than one object with just one database query, while every call to an object's save method will hit the database!
Another point worth mentioning is, that django's pre_save & post_save signals are only sent when you call an object's save-method, but not upon QuerySet.update().
Coming to the conflict issues you describe, I think it would also be irritating if you hit 'save' and then you have to discover that afterwards some values are the same as when you changed them, but some that you left unchanged have changed?!? Of course it's up to you to modify the admin's save_model or the object's save method to the bahaviour you suggest.
The problem you described about Django Admin is essentially about updating a model instance using an outdated copy. It is easy to fix by adding a version number to each model instance and increment the number on each update. Then in the save method of the model, just make sure what you are saving is not behind what is already in the database.
I want to make sure when there are parallel writes to the same object, each write updates a different fields, they don't overwrite each other's values.
Depending on the application, this may or may not be a sensible thing. Saving a whole model even if only a single field is updated can often avoid breaking integrity of data. Thinking about the following example about travel itinerary of three-leg flight. Assume there is an instance of three fields representing three legs and three fields are SF->LA, LA->DC, DC->NY. Now if one update is to update the first two legs to SF->SD, SD->DC, and another update is to update the last two legs to LA->SJ, SJ->NY, and if you allow both to happen with update instead of saving the full model instance, you would come out with a broken itinerary of SF->SD, LA->SJ, SJ->NY.

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

django's post_save() and data from request

I desperately need to be able to get some information from the request array on post_save() for a certain model. Is it possible to do that somehow?
I think your best bet would be to override the save() method on that model and emit the post save signal along with the specific data you need to send. See Sending Signals to learn how.
An alternative (albeit a very dirty hackish way) would be to have an additional column in the model which saves the data you need to send out (although you would still have to override the save() method on the model).