Skip to content
本页目录

传统 Django 中就有基于类的视图的存在,DRF 中自然也有。优点也都差不多,即实现功能的模块化继承、封装,减少重复代码。接下来就用文章详情接口练练手。

首先在视图中新增下面的代码:

python
# article/views.py

from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status

from django.http import Http404

from article.models import Article
from article.serializers import ArticleDetailSerializer

class ArticleDetail(APIView):
    """文章详情视图"""

    def get_object(self, pk):
        """获取单个文章对象"""
        try:
            # pk 即主键,默认状态下就是 id
            return Article.objects.get(pk=pk)
        except:
            raise Http404

    def get(self, request, pk):
        article = self.get_object(pk)
        serializer = ArticleDetailSerializer(article)
        # 返回 Json 数据
        return Response(serializer.data)

    def put(self, request, pk):
        article = self.get_object(pk)
        serializer = ArticleDetailSerializer(article, data=request.data)
        # 验证提交的数据是否合法
        # 不合法则返回400
        if serializer.is_valid():
            # 序列化器将持有的数据反序列化后,
            # 保存到数据库中
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        article = self.get_object(pk)
        article.delete()
        # 删除成功后返回204
        return Response(status=status.HTTP_204_NO_CONTENT)
    
...

代码不复杂,就是提供了对文章详情的获取修改删除的 3 个方法,以及 1 个用于获取单个文章 model 的辅助方法。和之前说的一样,DRF 类视图与传统 Django 的区别,.get().put() 就是多了一个将对象序列化(或反序列化)的步骤。.delete() 方法因为不用返回实际数据,执行完删除动作就OK了。

从这个地方就可以看出,序列化器 serializer 不仅可以将数据进行序列化、反序列化,还包含数据验证、错误处理、数据库操作等能力。

序列化这个概念与具体语言无关。Python 或 JavaScript 对象转换为 Json 都称为序列化,反之为反序列化。Json 是两种语言传输信息的桥梁,一但信息到达,对方都需要将其还原为自身的数据结构。

由于详情接口需要返回完整的字段数据(与阉割版的文章列表接口不同),所以要重新给它定义一个序列化器:

python
# article/serializers.py

...

class ArticleDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = '__all__'

fields = '__all__' 代表要使用所有字段。

配置 urls.py

python
# article/urls.py

...

urlpatterns = [
    ...
    path('<int:pk>/', views.ArticleDetail.as_view(), name='detail'),
]

这就可以了。

发送一个请求试试:

python
# 再次提醒:建议用 Postman 来发送 web 请求
# 教程为了轻便使用了httpie,读者改为Postman即可

> http http://127.0.0.1:8000/api/article/1/
        
HTTP/1.1 200 OK
Allow: GET, PUT, DELETE, HEAD, OPTIONS
Content-Length: 141
Content-Type: application/json
...
{
    "body": "Maybe say somthing here...",
    "created": "2020-06-15T09:24:18Z",
    "id": 1,
    "title": "My first post",
    "updated": "2020-06-15T09:24:38.622789Z"
}

再试试修改文章接口:(注意请求方式变成了 PUT

python
> http PUT http://127.0.0.1:8000/api/article/1/ title=something... body=changed...
        
HTTP/1.1 200 OK
...
{
    "body": "changed...",
    "created": "2020-06-15T09:24:18Z",
    "id": 1,
    "title": "something...",
    "updated": "2020-07-02T07:05:49.554354Z"
}

再试试把它删除掉:

python
> http DELETE http://127.0.0.1:8000/api/article/1/
        
HTTP/1.1 204 No Content
Allow: GET, PUT, DELETE, HEAD, OPTIONS
...

这篇 id=1 的文章就被删除掉了。

通用视图

对数据的增删改查是几乎每个项目的通用操作,因此可以通过 DRF 提供的 Mixin 类直接集成对应的功能。

修改一下 ArticleDetail 视图:

python
# article/views.py

...
from rest_framework import mixins
from rest_framework import generics

class ArticleDetail(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):
    """文章详情视图"""
    queryset = Article.objects.all()
    serializer_class = ArticleDetailSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

使用 Mixin 已经足够简单了,但我们还可以让它更简单:

python
# article/views.py

...
class ArticleDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Article.objects.all()
    serializer_class = ArticleDetailSerializer

发送请求试试,功能和最开头那个继承 APIView 的视图是完全相同的。

列表接口也可以做同样的修改:

python
# article/views.py

...
class ArticleList(generics.ListCreateAPIView):
    queryset = Article.objects.all()
    serializer_class = ArticleListSerializer

除了上述介绍的以外,框架还提供 ListModelMixinCreateModelMixin 等混入类或通用视图,覆盖了基础的增删改查需求。