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.
Related
I have a django form (formA) that inherits from modelform.
In another app, I have a form that derives from that form (formB).
This all works ok, including obtaining fields from the base class formA, but when the clean method is called, it doesn't call the formA clean method. Instead it skips formA, and calls django's modelform.clean.
I can create a clean method in formB and this is called, but calling the super().clean() method also steps immediately into django's modelform.clean.
Why might this be happening and what do I have to do to get the correct inheritance chain for the clean method?
MTIA
It turned out that the different apps were in different venv prefixes, and this was why the inheritance wasn't working.
So, all fixed now.
When the user creates a product, multiple actions have to be done in save() method before calling super(Product,self).save(*args,**kwargs).
I'm not sure if I should use just one pre_save signal to do all these actions or it is better to create a signal for each of these actions separately.
Simple example (I'm going to replace save overrides by signals):
class Product(..):
def save(...):
if not self.pk:
if not self.category:
self.category = Category.get_default()
if not self.brand:
self.brand = 'NA'
super(Product,self).save(*args,**kwargs)
...
SO
#receiver(pre_save,sender=Product)
def set_attrs(instance,**kwargs):
if kwargs['created']:
instance.category = Category.get_default()
instance.brand = 'NA'
OR
#receiver(pre_save,sender=Product)
def set_category(instance,**kwargs):
if kwargs['created']:
instance.category = Category.get_default()
#receiver(pre_save,sender=Product)
def set_brand(instance,**kwargs):
if kwargs['created']:
instance.brand = 'NA'
This is just simple example. In this case, the general set_attrs should be probably enough but there are more complex situations with different actions like creating userprofile for user and then userplan etc.
Is there some best practice advice for this? Your opinions?
To put the facts out simply, it could be pointed out as a single piece of advice,
If action on one model's instance affects another model, signals are the cleanest way to go about. This is an example where you can go with a signal, because you might want to avoid some_model.save() call from within the save() method of another_model, if you know what I mean.
To elaborate on an example, when overriding save() methods, common task is to create slugs from some fields in the model. If you are required to implement this process on multiple models, then using a pre_save signal would be a benefit, rather than hard-coding in save() method of each models.
Also, on bulk operations, these signals and methods are not necessarily called.
From the 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.
For more reference,
Django override save() or signals?
Overriding predefined model methods
Django: signal or model method?
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.
I'm overriding Django's get_query_set function on one of my models dynamically. I'm doing this to forcibly filter the original query set returned by Model.objects.all/filter/get by a "scenario" value, using a decorator. Here's the decorator's function:
# Get the base QuerySet for these models before we modify their
# QuerySet managers. This prevents infinite recursion since the
# get_query_set function doesn't rely on itself to get this base QuerySet.
all_income_objects = Income.objects.all()
# Figure out what scenario the user is using.
current_scenario = Scenario.objects.get(user=request.user, selected=True)
# Modify the imported income class to filter based on the current scenario.
Expense.objects.get_query_set = lambda: all_expense_objects.filter(scenario=current_scenario)
# Call the method that was initially supposed to
# be executed before we were so rudely interrupted.
return view(request, **arguments)
I'm doing this to DRY up the code, so that all of my queries aren't littered with an additional filter. However, if the scenario changes, no objects are being returned. If I kill all of my python processes on my server, the objects for the newly select scenario appear. I'm thinking that it's caching the modified class, and then when the scenario changes, it's applying another filter that will never make sense, since objects can only have one scenario at a time.
This hasn't been an issue with user-based filters because the user never changes for my session. Is passenger doing something stupid to hold onto class objects between requests? Should I be bailing on this weird design pattern and just implement these filters on a per-view basis? There must be a best practice for DRYing filters up that apply across many views based on something dynamic, like the current user.
What about creating a Manager object for the model which takes the user as an argument where this filtering is done. My understanding of being DRY w/ Django querysets is to use a Model Manager
#### view code:
def some_view(request):
expenses = Expense.objects.filter_by_cur_scenario(request.user)
# add additional filters here, or add to manager via more params
expenses = expenses.filter(something_else=True)
#### models code:
class ExpenseManager(models.Manager):
def filter_by_cur_scenario(self, user):
current_scenario = Scenario.objects.get(user=request.user, selected=True)
return self.filter(scenario=current_scenario)
class Expense(models.Model):
objects = ExpenseManager()
Also, one quick caveat on the manager (which may apply to overriding get_query_set): foreign relationships will not take into account any filtering done at this level. For example, you override the MyObject.objects.filter() method to always filter out deleted rows; A model w/ a foreignkey to that won't use that filter function (at least from what I understand -- someone please correct me if I'm wrong).
I was hoping to have this implementation happen without having to code anything in other views. Essentially, after the class is imported, I want to modify it so that no matter where it's referenced using Expense.objects.get/filter/all it's already been filtered. As a result, there is no implementation required for any of the other views; it's completely transparent. And, even in cases where I'm using it as a ForeignKey, when an object is retrieved using the aforementioned Expense.objects.get/filter/all, they'll be filtered as well.
Django view points to a function, which can be a problem if you want to change only a bit of functionality. Yes, I could have million keyword arguments and even more if statements in the function, but I was thinking more of an object oriented approach.
For example, I have a page that displays a user. This page is very similar to page that displays a group, but it's still not so similar to just use another data model. Group also has members etc...
One way would be to point views to class methods and then extend that class. Has anyone tried this approach or has any other idea?
I've created and used my own generic view classes, defining __call__ so an instance of the class is callable. I really like it; while Django's generic views allow some customization through keyword arguments, OO generic views (if their behavior is split into a number of separate methods) can have much more fine-grained customization via subclassing, which lets me repeat myself a lot less. (I get tired of rewriting the same create/update view logic anytime I need to tweak something Django's generic views don't quite allow).
I've posted some code at djangosnippets.org.
The only real downside I see is the proliferation of internal method calls, which may impact performance somewhat. I don't think this is much of a concern; it's rare that Python code execution would be your performance bottleneck in a web app.
UPDATE: Django's own generic views are now class-based.
UPDATE: FWIW, I've changed my opinion on class-based views since this answer was written. After having used them extensively on a couple of projects, I feel they tend to lead to code that is satisfyingly DRY to write, but very hard to read and maintain later, because functionality is spread across so many different places, and subclasses are so dependent on every implementation detail of the superclasses and mixins. I now feel that TemplateResponse and view decorators is a better answer for decomposing view code.
I needed to use class based views, but I wanted to be able to use the full name of the class in my URLconf without always having to instantiate the view class before using it. What helped me was a surprisingly simple metaclass:
class CallableViewClass(type):
def __call__(cls, *args, **kwargs):
if args and isinstance(args[0], HttpRequest):
instance = super(CallableViewClass, cls).__call__()
return instance.__call__(*args, **kwargs)
else:
instance = super(CallableViewClass, cls).__call__(*args, **kwargs)
return instance
class View(object):
__metaclass__ = CallableViewClass
def __call__(self, request, *args, **kwargs):
if hasattr(self, request.method):
handler = getattr(self, request.method)
if hasattr(handler, '__call__'):
return handler(request, *args, **kwargs)
return HttpResponseBadRequest('Method Not Allowed', status=405)
I can now both instantiate view classes and use the instances as view functions, OR I can simply point my URLconf to my class and have the metaclass instantiate (and call) the view class for me. This works by checking the first argument to __call__ – if it's a HttpRequest, it must be an actual HTTP request because it would be nonsense to attept to instantiate a view class with an HttpRequest instance.
class MyView(View):
def __init__(self, arg=None):
self.arg = arg
def GET(request):
return HttpResponse(self.arg or 'no args provided')
#login_required
class MyOtherView(View):
def POST(request):
pass
# And all the following work as expected.
urlpatterns = patterns(''
url(r'^myview1$', 'myapp.views.MyView', name='myview1'),
url(r'^myview2$', myapp.views.MyView, name='myview2'),
url(r'^myview3$', myapp.views.MyView('foobar'), name='myview3'),
url(r'^myotherview$', 'myapp.views.MyOtherView', name='otherview'),
)
(I posted a snippet for this at http://djangosnippets.org/snippets/2041/)
If you're simply displaying data from models, why not use the Django Generic Views? They're designed to let you easy show data from a model without having to write your own view and stuff about mapping URL paramaters to views, fetching data, handling edge cases, rendering output, etc.
You can always create a class, override the __call__ function and then point the URL file to an instance of the class. You can take a look at the FormWizard class to see how this is done.
Unless you want to do something a little complex, using the generic views are the way to go. They are far more powerful than their name implies, and if you are just displaying model data generic views will do the job.
Sounds to me like you're trying to combine things that shouldn't be combined. If you need to do different processing in your view depending on if it's a User or Group object you're trying to look at then you should use two different view functions.
On the other hand there can be common idioms you'd want to extract out of your object_detail type views... perhaps you could use a decorator or just helper functions?
-Dan
Generic views will usually be the way to go, but ultimately you're free to handle URLs however you want. FormWizard does things in a class-based way, as do some apps for RESTful APIs.
Basically with a URL you are given a bunch of variables and place to provide a callable, what callable you provide is completely up to you - the standard way is to provide a function - but ultimately Django puts no restrictions on what you do.
I do agree that a few more examples of how to do this would be good, FormWizard is probably the place to start though.
If you want to share common functionality between pages I suggest you look at custom tags. They're quite easy to create, and are very powerful.
Also, templates can extend from other templates. This allows you to have a base template to set up the layout of the page and to share this between other templates which fill in the blanks. You can nest templates to any depth; allowing you to specify the layout on separate groups of related pages in one place.
You can use the Django Generic Views. You can easily achieve desired functionality thorough Django generic Views