首页 > 代码库 > API(六)之ViewSets & Routers
API(六)之ViewSets & Routers
REST框架包括一个对ViewSets
的抽象处理,它允许开发人员集中精力于modeling the state and interactions of the API, and leave the URL construction to be handled automatically, based on common conventions.
ViewSet
类与View
类几乎是一样的,除了它们所提供的操作,例如read
,或者update
,而不是 method handlers,如get
或put
。
一个ViewSet
类在最后一刻只被绑定到一组 method handlers,当它被实例化成一组视图时,通常使用一个Router
类来为你处理定义URL conf的复杂性。
重构使用ViewSets
我们来看看我们目前的视图,并将它们重构成视图集。
首先,让我们把UserList
和UserDetail
视图重构成一个单一的UserViewSet
。我们可以删除这两个视图,并用一个类替换它们:
from rest_framework import viewsets class UserViewSet(viewsets.ReadOnlyModelViewSet): """ This viewset automatically provides `list` and `detail` actions. """ queryset = User.objects.all() serializer_class = UserSerializer
在这里,我们已经使用ReadOnlyModelViewSet
类来自动提供默认的“只读”操作。我们仍然按照我们使用常规视图的方式设置queryset
和serializer_class
属性,但是我们不再需要向两个不同的类提供相同的信息。
接下来我们要更换SnippetList
,SnippetDetail
和SnippetHighlight
视图类。我们可以删除三个视图,并再次用一个类替换它们。
from rest_framework.decorators import detail_route class SnippetViewSet(viewsets.ModelViewSet): """ This viewset automatically provides `list`, `create`, `retrieve`, `update` and `destroy` actions. Additionally we also provide an extra `highlight` action. """ queryset = Snippet.objects.all() serializer_class = SnippetSerializer permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,) @detail_route(renderer_classes=[renderers.StaticHTMLRenderer]) def highlight(self, request, *args, **kwargs): snippet = self.get_object() return Response(snippet.highlighted) def perform_create(self, serializer): serializer.save(owner=self.request.user)
这一次,我们已经使用了ModelViewSet
类来获取完整的默认的读写操作。
请注意,我们还使用@detail_route
装饰器创建一个名为的自定义操作highlight
。此装饰器可用于添加任何异于标准的create
/ update
/ delete
样式的自定义端点。
使用@detail_route
装饰器的自定义操作默认响应GET
请求。如果我们想要一个响应POST
请求的动作,我们可以使用methods
参数。
默认情况下,自定义操作的URL取决于它本身的方法名。如果要更改URL的构造方式,可以将url_path作为decorator的一个关键字参数。
明确地将ViewSets绑定到URL
当我们定义URLConf时,handler methods 只能绑定到这些操作上。为了看看在底层发生了什么事情,让我们首先从ViewSets开始明确地创建一组视图。
在urls.py
文件中,我们将ViewSet
类绑定到一组具体的视图中。
from snippets.views import SnippetViewSet, UserViewSet, api_root from rest_framework import renderers snippet_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‘ })
请注意,我们从每个ViewSet类创建多个视图的方式,通过将http方法绑定到每个视图所需的操作。
现在我们将资源绑定到具体的视图中,我们可以像往常一样使用URL conf注册视图。
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‘) ])
使用路由器
因为我们使用ViewSet
类而不是View
类,我们实际上不需要自己设计URL。将资源连接到views和urls的约定可以使用Router
类自动处理。我们需要做的就是使用a router注册相应的视图集,然后让它执行其余操作。
这是我们的重新连接的urls.py
文件。
from django.conf.urls import url, include from snippets import views from rest_framework.routers import DefaultRouter # 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 are now determined automatically by the router. # Additionally, we include the login URLs for the browsable API. urlpatterns = [ url(r‘^‘, include(router.urls)), url(r‘^api-auth/‘, include(‘rest_framework.urls‘, namespace=‘rest_framework‘)) ]
Registering the viewsets with the router is similar to providing a urlpattern. We include two arguments - the URL prefix for the views, and the viewset itself.
我们使用的DefaultRouter
类会自动创建API根视图,因此我们现在可以从views
模块中删api_root方法。
views 与 viewsets之间的权衡
使用viewsets是一个非常有用的抽象。它有助于确保您的API中的URL约定是一致的,最大限度地减少您需要编写的代码量,并允许您专注于API提供的交互和表现层,而不是URL conf的具体内容。
That doesn‘t mean it‘s always the right approach to take. There‘s a similar set of trade-offs to consider as when using class-based views instead of function based views. Using viewsets is less explicit than building your views individually.
API(六)之ViewSets & Routers