第 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 接口,能夠看到返回了更加詳細的博客文章數據了:


關注公衆號加入交流羣
相關文章
相關標籤/搜索