I have a pre-save signal for one of my models. This pre-save signal does some background API activity to syndicate new and updated objects to service providers and return meaningless data for us to store as references in the places of the original data.
The new and update methods are different in the API.
Ideally, if a user were to perform an update they would be clearing the meaningless data from a field and typing over it. My signal would need to know which fields were updated to send changes for just those fields, as sending all fields in an update would send meaningless references as the raw data in addition to the updates.
The pre-save signal has the argument update_fields. I searched for some details and found that this argument may include all fields when an update is performed.
Regarding update_fields as the docs have little information on this
When creating an object, does anything get passed to update_fields?
When updating an object, do all fields get passed to update_fields, or just the ones that were updated?
Is there some other suggestions on how to tackle this? I know post_save has the created argument, but I'd prefer to operate on the data before it's saved.
When creating an object, does anything get passed to update_fields?
No.
When updating an object, do all fields get passed to update_fields, or just the ones that were updated?
Depends who is calling the save() method. By default, Django doesn't set update_fields. Unless your code calls save() with the update_fields argument set, it will rewrite all the fields in the database and the pre_save signal will see update_fields=None.
My signal would need to know which fields were updated to send changes for just those fields.
Unless you are controlling what calls the save() method on the object, you will not get this information using update_fields. The purpose of that argument is not to let you track which fields have changed - rather it is to facilitate efficient writing of data when you know that only certain columns in the database need to be written.
Related
I want to create a new model which uses the https://developer.microsoft.com/en-us/graph/graph-explorer api as a data source as i want to have additional info on the user.
Using a computed property on the model does not work as it is going to query for each instance in the set.
So, i want to have the model relate to a new model which has the api as it´s data source.
I could not find anything on this topic, besides maybe abusing the from_db() method, if this even works.
It appears that what you're trying to do is to cache data from an external API that relates to, and augments/enriches, your user model data. If so, you can simply use a custom user model (instead of Django's default; this is a highly-recommended practice anyway) and then simply store the external API data in serialized form in a TextField attribute of your custom user model (let's call it user_extras; you can write model methods that serializes and deserializes this field for convenience upon access in your views).
The key challenge then is how to keep user_extras fresh, without doing something terrible performance-wise or hitting some constraint like API call limits. As you said, we can't do API queries in computed properties. (At least not synchronously.) One option then is to have a batch job/background task that regularly goes through your user database to update the user_extras in some controlled, predictable fashion.
I want to do some custom actions before a user is created. and I thought of using the pre_save signal for that. And in case one of those action would raise an exception stop the transaction, abort creating the user etc.
Is this the way to go? would it abort the save if something fails in this step (this is the required behavior) I suspect so but couldn't find docs about it.
What is the best practice for getting the future user.id. from my understanding it doesn't exists yet in pre-save but I need it as input for some of the extra custom actions.
Thanks
From the docs:
There's no way to tell what the value of an ID will be before you call
save(), because the value is determined by your database, not by
Django.
So if your pre-save processing requires the user.id, I am afraid this is not possible.
Here is the two part answer:
Yes, raising an exception in the pre_save() signal will abort the call to the save() method. For example, I did this in my pre_save():
if SOME_TEST:
raise exceptions.ParseError(detail="I reject your data. Here is why.")
Note: This was using the DRF exceptions, but I will assume you know how to raise whatever exception you prefer.
You could use the post_save() signal to get the id AFTER the save() method. But if that will not work for you, then below is my original explanation of some tactics to do what you want in the pre_save():
In the pre_save you can access User.objects.all() (if you import the User model, of course). If you are using the standard User model then the User.id value is AUTO INCREMENT. So if you NEED to know what the value WILL be for the new user, just get the highest User.id currently in the database (ie: the id of the last user created). Here is a simple query to do this:
last_user = User.objects.latest('id')
Then of course, last_user.id will give you the value of the id for the last user created and if you add one to that you will have the next user id. Of course, if you have a busy site you may have some problems with simultaneous user creation. The risk is that this value is received by two (or more) User creation attempts and one (or more) of them ends up wrong.
Of course, you can always set another field to be primary_key=True and this will replace the id field on the model. Then you can devise any sort of indexing mechanism that you can dream up! You could use a hash value of a unique characteristic. For example: The User.username has a Unique constraint, you could hash or hex encode that as a numeric ID to pre-determine the User.id. Or you could leave the id field in place and set it manually in the pre_save by assigning a value to obj.id. It could be anything you want. Even a hash value!
I have a database model that is being updated based on changes in remote data (via an HTML scraper).
I want to maintain a field called changed - a timestamp denoting when the last time that model's values changed from what they were previously (note that this is different from auto_now as these fields are updated every time a model's save method is called).
Here is my question:
In a model's save method, is there a straightforward way to detect if a model instance's current values are different from the values in the database? Or, are there any alternative methods to easily maintain a changed timestamp?
If you save your instance through a form, you can check form.has_changed().
http://code.activestate.com/pypm/django-dirtyfields/
Tracks dirty/changed fields on a django model instance.
Sounds to me like what you want is Signals: http://docs.djangoproject.com/en/1.2/topics/signals/
You could use a post_save signal to update a related field in another model to store the previous value. Then on the next go-round you'd have something to compare.
You might try computing a checksum of the record values when you save them. Then when you read it later, recompute the checksum and see if it has changed. Perhaps the crc32 function in the Python zlib standard module. (I'm not sure what kind of performance this would have. So you may want to investigate that.)
This library has tracks FK lookups.
https://github.com/mmilkin/django_dirty_bits
Forms have Fields, Fields have a value. However, they only get a value after the form has been submitted.
How should I store this value? Should I give every field a value attribute, field.value,
leave it as None prior to posting, and fill it in afterwords?
Omit it completely, and dynamically add it?
Store it on the form instead, like form.data['field'].
Create a a wrapper class FieldWithData to avoid any inconsistent states (if you have an object of this type, you know it has data) and allows me to set the data in the initializer rather than accessing attributes directly (although I guess this isn't so different from using a setter)
How should I provide access to the field data through the Form object? Options:
form.fields['name'].value (how it's presently being stored internally)
form.data['field'] (create a proxy "data" class that retrieves the real data off the field, or re-arrange the internals to actually store the data like this)
form.field.value - looks fairly nice, but then I'd have two references to the same field, one as form.field and one as form.fields['field'] which I need internally so that I can iterate over them
Too many design decisions. Driving me nuts. This is what sucks about solo'ing a project.
It really depends on how you interact with the structures in question. Do you manipulate Form and Field objects prior to assigning them values? Do you need to frequently iterate over all the given Fields? Do you need Form once it's been submitted? Etc.
I'd suggest writing some/all of the code that uses Form and figure out how you want to interact with Form data, and what your ideal interface would look like.
I would keep a form's definition and the form's values from submission separate. I.e. I would not have a value attribute on the Field(Definition) objects.
To work with submitted values, I would probably use a dict. You could let the Form class handle the creation of this dict:
# assuming my_form is a Form object and request represents the HTTP request
form_values = my_form.values_from_request(request)
print(form_values["Name"])
The values_from_request method would iterate through the Form's Field(Definition)s to get the submitted data from the HTTP request. The method could also do stuff like validation and data type conversion.
I'm having trouble wrapping my head around this. Right now I have some models that looks kind of like this:
def Review(models.Model)
...fields...
overall_score = models.FloatField(blank=True)
def Score(models.Model)
review = models.ForeignKey(Review)
question = models.TextField()
grade = models.IntegerField()
A Review is has several "scores", the overall_score is the average of the scores. When a review or a score is saved, I need to recalculate the overall_score average. Right now I'm using a overridden save method. Would there be any benefits to using Django's signal dispatcher?
Save/delete signals are generally favourable in situations where you need to make changes which aren't completely specific to the model in question, or could be applied to models which have something in common, or could be configured for use across models.
One common task in overridden save methods is automated generation of slugs from some text field in a model. That's an example of something which, if you needed to implement it for a number of models, would benefit from using a pre_save signal, where the signal handler could take the name of the slug field and the name of the field to generate the slug from. Once you have something like that in place, any enhanced functionality you put in place will also apply to all models - e.g. looking up the slug you're about to add for the type of model in question, to ensure uniqueness.
Reusable applications often benefit from the use of signals - if the functionality they provide can be applied to any model, they generally (unless it's unavoidable) won't want users to have to directly modify their models in order to benefit from it.
With django-mptt, for example, I used the pre_save signal to manage a set of fields which describe a tree structure for the model which is about to be created or updated and the pre_delete signal to remove tree structure details for the object being deleted and its entire sub-tree of objects before it and they are deleted. Due to the use of signals, users don't have to add or modify save or delete methods on their models to have this management done for them, they just have to let django-mptt know which models they want it to manage.
You asked:
Would there be any benefits to using Django's signal dispatcher?
I found this in the django docs:
Overridden model methods are not called on bulk operations
Note that the delete() method for an object is not necessarily called
when deleting objects in bulk using a QuerySet or as a result of a
cascading delete. To ensure customized delete logic gets executed, you
can use pre_delete and/or post_delete signals.
Unfortunately, there isn’t a workaround when creating or updating
objects in bulk, since none of save(), pre_save, and post_save are
called.
From: Overriding predefined model methods
Small addition from Django docs about bulk delete (.delete() method on QuerySet objects):
Keep in mind that this will, whenever possible, be executed purely in
SQL, and so the delete() methods of individual object instances will
not necessarily be called during the process. If you’ve provided a
custom delete() method on a model class and want to ensure that it is
called, you will need to “manually” delete instances of that model
(e.g., by iterating over a QuerySet and calling delete() on each
object individually) rather than using the bulk delete() method of a
QuerySet.
https://docs.djangoproject.com/en/1.11/topics/db/queries/#deleting-objects
And bulk update (.update() method on QuerySet objects):
Finally, realize that update() does an update at the SQL level and,
thus, does not call any save() methods on your models, nor does it
emit the pre_save or post_save signals (which are a consequence of
calling Model.save()). If you want to update a bunch of records for a
model that has a custom save() method, loop over them and call save()
https://docs.djangoproject.com/en/2.1/ref/models/querysets/#update
If you'll use signals you'd be able to update Review score each time related score model gets saved. But if don't need such functionality i don't see any reason to put this into signal, that's pretty model-related stuff.
It is a kind sort of denormalisation. Look at this pretty solution. In-place composition field definition.