Welcome to my personal website: www.comingnext.cn
Objective
In this series of articles, I wrote the Django view in the first and second articles, using a function-based approach, and each view function was preceded by a django-rest-framework with an adorner @api_view. Then in the third article, we started to change the function-based view to a class-based view, and then found that the view section reduced a lot of the amount of code.
In this article, I'm going to talk about another class-based view, which is more abstract, and it can be said that the amount of code is reduced. OK, nonsense not much to say, first into the theme ~
Using the Viewsets refactoring view
Let's introduce this viewsets. Viewsets, translation can be said to be a view set, that is, a collection of several views.
Take this project as an example, before we look at all user lists, we'll write a view class userlist and set a pattern for it in urls.py and then as_view use it. Then look at the individual user's details page and then write another Userdetail view class and add a URL pattern. Also note that both view classes are inherited generics. Xxxapiview. With Viewsets we can merge userlist and userdetail into Userviewset view class, and the inherited class is changed to Viewsets. Readonlymodelviewset, this is a view set.
Or is it a little crazy? Okay, here's a look at the code. Edit snippets/view.py, import viewsets and use Userviewset to replace UserList and Userdetail:
from Import viewsets class Userviewset (viewsets. Readonlymodelviewset): "" " viewset automatically provides list and detail actions " "" = User.objects.all () = Userserializer
The value of Queryset and Serializer_class is the same as the original. Because the API for the user is read-only, we have inherited a Readonlymodelviewset class, which sets up the original two view classes. Inside the original class:
Queryset == Userserializer
This part is duplicated code, so the view set to implement the view class our code volume really reduced, more concise.
The Viewset class is virtually the same as the view class, but it provides read or update operations rather than HTTP actions such as GET or put. At the same time, Viewset provides us with a default URL structure that allows us to focus more on the API itself.
The above paragraph, is the official document inside said, want to look like this even to understand, but if we look at the source code may be able to understand better. Because I use pycharm, so it is convenient to view the source code, hold down the CTRL key and then click on the mouse will automatically jump, first look at the Readonlymodelviewset, found it is this:
class Readonlymodelviewset (mixins. Retrievemodelmixin, mixins. Listmodelmixin, genericviewset): "" " A viewset that provides default ' list () ' and ' Retrieve () ' actions. """ Pass
found that the original useful to say before the mixins, so just said Viewset class and view class is actually almost the same. But here's one more Genericviewset class is the new content, continue CTRL Click to view its code, found that it is only a pass and then there is no other operation, but you can continue to see its parent viewsetmixin source to understand Viewsets, Then you can see that this viewsetmixin actually overrides the As_view method:
@classonlymethod def As_view (CLS, Actions=none, * *Initkwargs) :"" "Because of the The-by class Based views create a closure around the instantiated view, we need to totally reimplement '. As_view ', and slightly Modify the View function is created and returned. """ ...
We usually use the view class, when writing urls.py, just a xxx.as_view (), now use viewsets, need to pass in parameters, probably like this:
Userviewset.as_view ({'get''list'})
Then the URL is configured, that is, the viewset gives us the default URL structure. Of course, this is not a full URL pattern, but it will be complete later.
Just now the user's two view class is merged into the view set, then the snippet of several view class operations are similar. Use the view set Snippetviewset instead of the snippetlist, Snippetdetail and Snippethighlight three view classes:
fromRest_framework.decoratorsImportDetail_routeclassSnippetviewset (viewsets. Modelviewset):"""viewset automatically provides ' list ', ' Create ', ' Retrieve ', ' Update ' and ' destroy ' actions. At the same time we manually add an extra ' highlight ' action to view the highlighted code snippet"""Queryset=Snippet.objects.all () Serializer_class=Snippetserializer permission_classes=(Permissions. Isauthenticatedorreadonly, Isownerorreadonly,) @detail_route (renderer_classes=[renderers. Statichtmlrenderer])defHighlight (self, request, *args, * *Kwargs): Snippet=Self.get_object ()returnResponse (snippet.highlighted)defperform_create (self, Serializer): Serializer.save (owner=self.request.user)
Because the view highlight is not like other actions, Django-rest-framework is not packaged for us, so we need to add this extra action ourselves, remember to precede the method with the adorner @detail_route, This decorator is used to create custom actions, of course, our custom actions can not be create/update/delete these standards, otherwise there will be conflicts.
Also, the action defined by the @detail_route adorner is the GET request by default, and other request methods can be passed to the adorner with the methods parameter. Similarly, by default, the URL for a custom action depends on the method name itself. If you want to change how the URL should be constructed, you can use Url_path as the Decorator keyword parameter.
Finally also pay attention to inherit the class is Modelviewset and just also a bit different, why replace this, also can see source code can knows:
class Modelviewset (mixins. Createmodelmixin, mixins. Retrievemodelmixin, mixins. Updatemodelmixin, mixins. Destroymodelmixin, mixins. Listmodelmixin, genericviewset):
Bind viewsets explicitly to the URL
According to the above, the URL pattern for each view set requires that we pass in the parameters in As_view and replace the snippets/urls.py code with the following:
fromDjango.conf.urlsImportUrl,include fromSnippets.viewsImportSnippetviewset, Userviewset, Api_root fromRest_frameworkImportrenderers fromRest_framework.urlpatternsImportformat_suffix_patternssnippet_list=Snippetviewset.as_view ({'Get':'List', 'Post':'Create'}) Snippet_detail=Snippetviewset.as_view ({'Get':'Retrieve', 'put':'Update', 'Patch':'partial_update', 'Delete':'Destroy'}) Snippet_highlight=Snippetviewset.as_view ({'Get':'Highlight'}, Renderer_classes=[renderers. Statichtmlrenderer]) user_list=Userviewset.as_view ({'Get':'List'}) User_detail=Userviewset.as_view ({'Get':'Retrieve'}) Urlpatterns=format_suffix_patterns ([url (r'^$', api_root), url (r'^snippets/$', Snippet_list, Name='snippet-list'), url (r'^snippets/(? p<pk>[0-9]+)/$', Snippet_detail, Name='Snippet-detail'), url (r'^snippets/(? p<pk>[0-9]+)/highlight/$', Snippet_highlight, Name='Snippet-highlight'), url (r'^users/$', User_list, Name='user-list'), url (r'^users/(? p<pk>[0-9]+)/$', User_detail, Name='User-detail')])
OK, here we go. The transformation of the view has been completed, you can start the server test, our project function is the same as before.
Using routers
But see urls.py code, we may find that the problem is that our view class code is less concise, but the url.py of the code seems to be more ah, to bind so many actions, so it seems to be not much improvement?
That's true. But we are in the development of Python, of course, is short and short, yes, the author of Django-rest-framework also think so, so we have ready-made wheels can be used. This wheel is another protagonist of this article--routers. It is also simple and rough to use, rewrite urls.py:
fromDjango.conf.urlsImportURL, include fromSnippetsImport views fromRest_framework.routersImportDefaultrouter#Create a router and register our viewsets with it.Router =Defaultrouter () router.register (R'Snippets', views. Snippetviewset) Router.register (R'Users', views. Userviewset)#The API URLs is now determined automatically by the router.#Additionally, we include the login URLs for the browsable API.Urlpatterns =[url (r'^', include (Router.urls)),]
This is done, the code is a lot less, even the original used to set the suffix of the following line of code is not required.
Urlpatterns = Format_suffix_patterns (urlpatterns)
And this Defaultrouter class will automatically help us create the API root view, which means that the Api_root method in view.py can also be removed.
The amount ... There's a lot of things that routers do for us. But that's why I said in the preface of the article that using viewsets would be more abstract than the original view.
It's going to happen, but what's going on in there, we don't know, like where's the API suffix? The above we wrote:
Snippet_list = Snippetviewset.as_view ({ 'get'list ', 'post''create'})
Are all these bindings automatically generated? These are really defaultrouter help us do, how to do, we can still look at the source code to understand the approximate process. The first is the register method, we bind so many actions it is done two lines, look at its source code, found it is a method under the Baserouter class:
classBaserouter (object):def __init__(self): Self.registry= [] defRegister (self, prefix, viewset, base_name=None):ifBase_name isNone:base_name=self.get_default_base_name (Viewset) self.registry.append ((prefix, viewset, base_name)) ... @property defURLs (self):if notHasattr (Self,'_urls'): Self._urls=Self.get_urls ()returnSelf._urls
The change method generates the URL endpoint, which is/snippets and/users, based on the parameters passed in, and then it is stored in the registry list. And the end of this class is a method that can be used as an attribute URL, and this method calls the Get_urls () to generate all the URLs pattern, of course, this get_urls () Quilt class Simplerouter and sub-subclasses Defaultrouter rewrite. The Get_urls () in Simplerouter implements the 5 URL pattern, which is the original one:
URL (r'^snippets/$', snippet_list,name='snippet-list'), url (r'^snippets/(? p<pk>[0-9]+)/$', Snippet_detail, Name='Snippet-detail'), url (r'^snippets/(? p<pk>[0-9]+)/highlight/$', Snippet_highlight, Name='Snippet-highlight'), url (r'^users/$', User_list, Name='user-list'), url (r'^users/(? p<pk>[0-9]+)/$', User_detail, Name='User-detail')
The Get_urls () in Defaultrouter generates the Api_root URL pattern, and also adds a format suffix to these URL patterns, so we don't use format_suffix_patterns this thing ourselves.
Summarize
Of course, it is not necessary to use viewsets views instead of view, both have the advantage Viewsets save a lot of code and the URL pattern is not set by ourselves, but also bring some uncertainty, the effect of automation may sometimes be different from what you expected, So you want to choose which method to look at your own liking.
Well, the introduction of viewsets and routers is here, the API function is the same as before, so here do not show.
Django Authoring restful API (vi): Viewsets and routers