Django - call original and overriden save method - python

This may be a noobish question but it bothers me quite a lot (I'm quite new to both django and python)
In my django app, I overrided the save() method of a model to perform some interaction on the file system.
I created a form class like this :
class AddItemForm(ModelForm):
class Meta:
model = OriginalModel
So, in my views, when I call form.save(), eveything works fine.
But, when testing my app, I'd like to be able to call the original save() method to avoid creating plenty of files that I won't use and have to take care of.
What I tried is to create a savebis() method, in order to preserve the original save() method, but then how can I pass it to the ModelForm, so that I can call form.save() or form.savebis()?
EDIT : savebis() is already written and working in my model. I want to be able to call it from a modelform instance, but I don't know how to do this.

From your question, it sounds to me like you've got some optional processing that should occur in the Model's save method. As suggested in the question comments, just add a TESTING = True type constant to your settings.py file used during testing and check this value in the Model save method:
from django.conf import settings
class OriginalModel(Model):
...
def save(self, *args, **kwargs):
if not settings.TESTING:
# Do some file system interaction, but not during testing.
pass
# Now save instance as per normal.
return super(originalModel, self).save(*args, **kwargs)
If you go down the path of overriding the ModelForm save method then you have to replicate existing functionality of the ModelForm save method, and changing it to call you model's savebis() method instead of the save() method. I'd advise against going down this path as it make the code more complex than need be.

Related

How do I set a specific value for an attribute of a model every time I save it on Django?

I have a legacy project that saves models with save, bulk_create and other methods within the framework.
What is the best way to set a specific value for an attribute so that every time a record is saved the new value is also saved? This value is constructed based on other attributes of the instance that is being saved.
I pose this question because I'm not sure all ways that save is possible in Django except save and bulk_create and knowing that on bulk_create:
The model’s save() method will not be called, and the pre_save and
post_save signals will not be sent.
https://docs.djangoproject.com/en/1.8/ref/models/querysets/#bulk-create
As far as I know, there are 3 ways to create/update model instances (which are records in database tables):
Using the model instance method save().
Using the queryset methods create(), update(), get_or_create(), update_or_create() and bulk_create().
Using raw SQL or other low-level ways.
If you intend to calculate the value of a field when saving, you could override all of the methods I listed above.
Signals (like pre_create) are not a complete solution because they don't get triggered when bulk_create() is used and so some instance could get saved without the calculated attribute.
There is no django way (that I know) to intercept the third point I mentioned (raw SQL).
You did not elaborate on your use case, but (depending on your table size and change frequency) maybe you could also try:
run a periodical process (maybe using crontab) that updates the calculated field of all model instances.
add a database trigger that calculates the field.
Legacy databases or systems or usually not fun to work with, so maybe you will have to settle for a sub-optimal solution.
You can set default value in your model's field using custom functions. For example you have a Post model that also has a field slug. You want default value for slug field to be auto generated from name field. You can write your model like below:
class Post(models.Model):
def generate_slug(self):
return slugify(self.name)
name = models.CharField()
description = models.TextField()
attachment = models.FileField()
slug = models.CharField(default=generate_slug)
This way when you create a new post, the slug field will be auto generated from the name field.
Another way to do that is to create a layer between your caller and the models(database layer) so you can add your logic there. With this you will narrow the possibilities to just the methods you expose in that layer and have control over what should happen everywhere in terms of database talk.
The best way to deal with this issue is to override the save method().
You can use as well raw sql queries , which can easily solve your problems as well
class Model(model.Model):
field1=models.CharField()
field2=models.CharField()
field3=models.CharField()
def myfunc (self):
pass
#
def save(self, *args, **kwargs):
q = MyModel.objects.select_related('fields1', 'field2', 'filed2').filter(related_field)
super(Model, self).save(*args, **kwargs)

What's the difference between these two ways to override the save() method in a Django ModelForm?

I've come across two methods of doing this. The accepted answer here suggests:
def save(self, *args, **kwargs):
instance = super(ModelClass, self).save(commit=False)
instance.my_stuff = manual_value
instance.save()
But the following, found here, seems more elegant:
def save(self, *args, **kwargs):
self.my_stuff = manual_value
super(ModelClass, self).save(*args, **kwargs)
Is there any reason to choose one over the other, other than the latter being one less line, such as a reason for running the parent save() first?
The two examples are doing different things. The first is saving the model form's save method, the second is overriding the model's save method.
It only makes sense to override the model's method if you want the value to be set every time the model is saved. If updating the field is related to the form, then overriding the form's save method is the correct thing to do.
In the model form's save method, you have to call save(commit=False) first to get the instance. I wouldn't worry about it being inelegant, it's a very common pattern in Django, and is documented here.
First one will create instance of model, without saving it, then you can add some value (that is required or not) and manually trigger save on that instance.
Second one will save some field on ModelForm (not on actual instance of your model) and then create + save instance of your model.
If in second one you're just setting value on form field that corresponds to model field edited in first example, that will almost work in same way.
Why almost? On second example you have to have that form field inside your form class, on first example you don't. If that field is required, and have been left empty, second example won't validate.
That being said, first example can add or change fields in your model that haven't appeared on form, second example can only modify fields that have been specified inside form class.

When are create and update called in djangorestframework serializer?

I'm currently implementing djangorestframework for my app RESTful API. After playing around with it, I still do not clearly understand what .create(self, validated_data) and .update(self, validated_data) used for in the serializer. As I understand, CRUD only calls the 4 main methods in viewsets.ModelViewSet: create(), retrive(), update(), and destroy().
I also have already tried to debug and print out stuff to see when the .create() and .update() methods are called in both ModelViewSet and ModelSerializer. Apparently, only the methods in ModelViewSet are called when I do the HTTP verbs. However, for ModelSerializer, I don't see any calls in those 2 methods. I just want to know what are those methods used for in ModelSerializer since I see that people override those methods a lot in the serializer.
You really must split things between the views and the serializer.
Serializers
The Serializer is a standalone object. It is used for converting a Django model (or any kind of python datastructure, actually) into a serialized form, and the other way around.
You may use it as such, wherever you want. It does not even need an actual HTTP request as long as you don't need URIs in your output.
The ModelSerializer subclass is a specialized kind of Serializer that adds "load-from-model" and "save-to-model" functionality.
The "save-to-model" entry point is the save() method. For easier overriding, its default implementation will delegate its work to either the create() or update() method of the serializer, depending on whether it is creating a new model instance, or updating one.
The purpose of that is customization: it gives you, the developer, the option to override just the create method, just the update method, or common behavior.
For instance, it allows you to do this kind of things:
def save(self, **kwargs):
# Will be done on every save
kwargs['last_changed'] = timezone.now()
return super().save(**kwargs)
def create(self, instance, data):
# Will only be done if a new object is being created
data['initial_creation'] = timezone.now()
return super().create(instance, data)
That's a basic example. There, the last_changed field will be set every time an object is saved, be it a creation or an update.
As a sidenote, you probably do not want to do that. Things such as setting "last_changed" fields should live in the view, not in the serializer.
Viewsets
In a completely different place, Django REST framework supplies Viewsets. Those are an organized collection of views, revolving around implementing a CRUD API for a model.
As such, it structures it functionality into a set of methods, namely create(), retrieve()/list(), update() and delete().
The main point being: there is no connection whatsoever between the viewset's create() method and the serializer's create() method.
It just happens that the default implementation of the viewset's methods uses a ModelSerializer and that the default implementation of that serializer's save() method delegates the job to methods that have the same name.
By the way, about the last_changed example, here is how you would do it in the view:
def perform_create(self, serializer):
now = timezone.now()
serializer.save(initial_creation=now, last_changed=now)
def perform_update(self, serializer):
serializer.save(last_changed=timezone.now())
That's functionally equivalent to the example above, but lives in the viewset.
Conclusion
So back to your question, the specific thing you should override depends on which object is responsible for the task you want to add.
If your custom behavior is part of the serialization process, that is, the process of converting raw data back into a proper Django model and saving it, then you should override the Serializer's methods.
If, on the other hand, your custom behavior is specific to your viewset, then you should override the Viewset's methods.
As a hint, you may ask yourself the following question: if I use the same serializer in another place (maybe another viewset), should it always display that behavior?
I finally understand how the .create() and .update() work in Serializer (especially ModelSerializer) and how they are connected to Viewsets (especially ModelViewSet). I just want clarify the concept more clearly if someone comes to this question.
Basically, the 4 methods CRUD in ModelViewSet: .create(), .retrieve(), .update(), and .destroy() will handle the calls from HTTP verbs. By default, the .create() and .update() from ModelViewSet will call the .create() and .update() from ModelSerializer by calling the .save() method from the BaseSerializer class.
The save() method will then determine whether it will call .create() or .update() in ModelSerializer by determining whether the object self.instance exists or not.
In the rest-api design create, read, update and delete is a standard.
There is not quite big difference in create and update.
Please refer to this and
see create() method will create an item.
and
update() method need to specify which item to be updated.

Django "emulate" database trigger behavior on bulk insert/update/delete

It's a self expaining question but here we go.
I'm creating a business app in Django, and i didn't wanted to "spread" all the logic across app AND database, but in the other hand, i didn't wanted to let the Database handle this task (its possible through the use of Triggers).
So I wanted to "reproduce" the behavior of the Databse Triggers, but inside the Model Class in Django (um currently using Django 1.4).
After some research, I figured out that with single objects, I could override the "save" and "delete" methods of "models.Model" class, inserting the "before" and "after" hooks so they could be executed before and after the parent's save/delete. Like This:
class MyModel(models.Model):
def __before(self):
pass
def __after(self):
pass
#commit_on_success #the decorator is only to ensure that everything occurs inside the same transaction
def save(self, *args, *kwargs):
self.__before()
super(MyModel,self).save(args, kwargs)
self.__after()
The BIG problem is with bulk operations. Django doesn't triggers the save/delete of the models when running the "update()"/"delete()" from it's QuerySet. Insted, it uses the QuerySet's own method. And to get a little bit worst, it doesn't trigger any signal either.
Edit:
Just to be a little more specific: the model loading inside the view is dynamic, so it's impossible to define a "model specific" way. In this case, I should create an Abstract Class and handle it there.
My last attempt was to create a custom Manager, and in this custom manager, override the update method, looping over the models inside the queryset, and trigering the "save()" of each model (take in consideration the implementation above, or the "signals" system). It works, but results in a database "overload" (imagine a 10k rows queryset being updated).
First, instead of overriding save to add __before and __after methods, you can use the built-in pre_save, post_save, pre_delete, and post_delete signals. https://docs.djangoproject.com/en/1.4/topics/signals/
from django.db.models.signals import post_save
class YourModel(models.Model):
pass
def after_save_your_model(sender, instance, **kwargs):
pass
# register the signal
post_save.connect(after_save_your_model, sender=YourModel, dispatch_uid=__file__)
pre_delete and post_delete will get triggered when you call delete() on a queryset.
For bulk updating, you'll have to manually call the function you want to trigger yourself, however. And you can throw it all in a transaction as well.
To call the proper trigger function if you're using dynamic models, you can inspect the model's ContentType. For example:
from django.contrib.contenttypes.models import ContentType
def view(request, app, model_name, method):
...
model = get_model(app, model_name)
content_type = ContentType.objects.get_for_model(model)
if content_type == ContenType.objects.get_for_model(YourModel):
after_save_your_model(model)
elif content_type == Contentype.objects.get_for_model(AnotherModel):
another_trigger_function(model)
With a few caveats, you can override the queryset's update method to fire the signals, while still using an SQL UPDATE statement:
from django.db.models.signals import pre_save, post_save
def CustomQuerySet(QuerySet):
#commit_on_success
def update(self, **kwargs):
for instance in self:
pre_save.send(sender=instance.__class__, instance=instance, raw=False,
using=self.db, update_fields=kwargs.keys())
# use self instead of self.all() if you want to reload all data
# from the db for the post_save signal
result = super(CustomQuerySet, self.all()).update(**kwargs)
for instance in self:
post_save.send(sender=instance.__class__, instance=instance, created=False,
raw=False, using=self.db, update_fields=kwargs.keys())
return result
update.alters_data = True
I clone the current queryset (using self.all()), because the update method will clear the cache of the queryset object.
There are a few issues that may or may not break your code. First of all it will introduce a race condition. You do something in the pre_save signal's receivers based on data that may no longer be accurate when you update the database.
There may also be some serious performance issues with large querysets. Unlike the update method, all models will have to be loaded into memory, and then the signals still need to be executed. Especially if the signals themselves have to interact with the database, performance can be unacceptably slow. And unlike the regular pre_save signal, changing the model instance will not automatically cause the database to be updated, as the model instance is not used to save the new data.
There are probably some more issues that will cause a problem in a few edge cases.
Anyway, if you can handle these issues without having some serious problems, I think this is the best way to do this. It produces as little overhead as possible while still loading the models into memory, which is pretty much required to correctly execute the various signals.

Django trigger parent model save when editing inline in admin

I have a model (Parent) with one-to-many relation to another model (Child). The save method of Parent model is overwritten:
class ParentModel(models.Model)
(...)
def save(self, *args, **kwargs):
(...) # Do sth with the model
super(ParentModel, self).save(*args, **kwargs)
class ChildModel(models.Model):
parent= models.ForeignKey(ParentModel)
In admin panel multiple Child models objects are displayed using StackedInline on Parent model's page. If a field of parent is edited and saved, the save method is called. When only child's fields are edited, Django do not call the save method of parent (as expected, because nothing changed).
What is the best way to force saving the parent, even if only child was edited (so that my overwritten method does it's stuff)?
You have a few solutions. Here goes, from simpler to more complex:
You could implement a custom save method for ChildModel that calls ParentModel.save.
You could also connect to your ChildModel's post_save or pre_save signal.
Now, these two solutions will prove annoying if you're going to update a lot of ChildModel instances at once, as you will be calling ParentModel.save several times, maybe without purpose.
You might then want to use the following:
Override your ParentModel's ModelAdmin.change_view to handle your logic; this is pretty tricky however.
I'm however pretty surprised by the behavior your're encountering, from checking the source, the object should be saved anyway; edited or not.

Categories