第 7 篇:文章详情的 API 接口

做者:HelloGitHub-追梦人物web

一旦咱们使用了视图集,并实现了 HTTP 请求对应的 action 方法(对应规则的说明见 使用视图集简化代码),将其在路由器中注册后,django-restframework 自动会自动为咱们生成对应的 API 接口。django

目前为止,咱们只实现了 GET 请求对应的 action——list 方法,所以路由器只为咱们生成了一个 API,这个 API 返回文章资源列表。GET 请求还能够用于获取单个资源,对应的 action 为 retrieve,所以,只要咱们在视图集中实现 retrieve 方法的逻辑,就能够直接生成获取单篇文章资源的 API 接口。api

贴心的是,django-rest-framework 已经帮咱们把 retrieve 的逻辑在 mixins.RetrieveModelMixin 里写好了,直接混入视图集便可:编辑器

class PostViewSet(  mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet ):  serializer_class = PostListSerializer  queryset = Post.objects.all()  permission_classes = [AllowAny] 复制代码

如今,路由会自动增长一个 /posts/:pk/ 的 URL 模式,其中 pk 为文章的 id。访问此 API 接口能够得到指定文章 id 的资源。post

实际上,实现各个 action 逻辑的混入类都很是简单,以 RetrieveModelMixin 为例,咱们来看看它的源码:性能

class RetrieveModelMixin:
 """  Retrieve a model instance.  """  def retrieve(self, request, *args, **kwargs):  instance = self.get_object()  serializer = self.get_serializer(instance)  return Response(serializer.data) 复制代码

retrieve 方法首先调用 get_object 方法获取需序列化的对象。get_object 方法一般状况下依据如下两点来筛选出单个资源对象:url

  1. get_queryset 方法(或者 queryset 属性, get_queryset 方法返回的值优先)返回的资源列表对象。
  2. lookup_field 属性指定的资源筛选字段(默认为 pk)。django-rest-framework 以该字段的值从 get_queryset 返回的资源列表中筛选出单个资源对象。 lookup_field 字段的值将从请求的 URL 中捕获,因此你看到文章接口的 url 模式为 /posts/:pk/,假设将 lookup_field 指定为 title,则 url 模式为 /posts/:title/,此时将根据文章标题获取单篇文章资源。

文章详情 Serializer

如今,假设咱们要获取 id 为 1 的文章资源,访问获取单篇文章资源的 API 接口 http://127.0.0.1:10000/api/posts/1/,获得以下的返回结果:spa

能够看到不少咱们须要在详情页中展现的字段值并无返回,好比文章正文(body)。缘由是视图集中指定的文章序列化器为 PostListSerializer,这个序列化器被用于序列化文章列表。由于展现文章列表数据时,有些字段用不上,因此出于性能考虑,只序列化了部分字段。rest

显然,咱们须要给文章详情写一个新的序列化器了:code

from .models import Category, Post, Tag
 class TagSerializer(serializers.ModelSerializer):  class Meta:  model = Tag  fields = [  "id",  "name",  ]  class PostRetrieveSerializer(serializers.ModelSerializer):  category = CategorySerializer()  author = UserSerializer()  tags = TagSerializer(many=True)   class Meta:  model = Post  fields = [  "id",  "title",  "body",  "created_time",  "modified_time",  "excerpt",  "views",  "category",  "author",  "tags",  ] 复制代码

详情序列化器和列表序列化器几乎同样,只是在 fields 中指定了更多须要序列化的字段。

同时注意,为了序列化文章的标签 tags,咱们新增了一个 TagSerializer,因为文章可能有多个标签,由于 tags 是一个列表,要序列化一个列表资源,须要将序列化器参数 many 的值指定为 True

动态 Serializer

如今新的序列化器写好了,但是在哪里指定呢?视图集中 serializer_class 属性已经被指定为了 PostListSerializer,那 PostRetrieveSerializer 应该指定在哪呢?

相似于视图集类的 queryset 属性和 get_queryset 方法的关系, serializer_class 属性的值也能够经过 get_serializer_class 方法返回的值覆盖,所以咱们能够根据不一样的 action 动做来动态指定对应的序列化器。

那么如何在视图集中区分不一样的 action 动做呢?视图集有一个 action 属性,专门用来记录当前请求对应的动做。对应关系以下:

HTTP 请求 对应 action 属性的值
GET list(资源列表)/ retrieve(单个资源)
PUT update
PATCH partial_update
DELETE destory

所以,咱们在视图集中重写 get_serializer_class 方法,写入咱们本身的逻辑,就能够根据不一样请求,分别获取相应的序列化器了:

class PostViewSet(  mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet ):  # ... 省略其余属性和方法  def get_serializer_class():  if self.action == 'list':  return PostListSerializer  elif self.action == 'retrieve':  return PostRetrieveSerializer  else:  return super().get_serializer_class() 复制代码

后续对于其余动做,能够再加 elif 判断,不过若是动做变多了,就会有不少的 if 判断。更好的作好是,给视图集加一个属性,用于配置 action 和 serializer_class 的对应关系,经过查表法查找 action 应该使用的序列化器。

class PostDetailViewSet(viewsets.GenericViewSet):
 # ... 省略其余属性和方法  serializer_class_table = {  'list': PostListSerializer,  'retrieve': PostRetrieveSerializer,  }   def get_serializer_class():  return self.serializer_class_table.get(  self.action, super().get_serializer_class()  ) 复制代码

如今,再次访问单篇文章 API 接口,能够看到返回了更加详细的博客文章数据了:


关注公众号加入交流群
相关文章
相关标签/搜索