Drf的Apiview、GenericView、Viewset和router的原理分析

来自CloudWiki
跳转至: 导航搜索


概述

从高到低的继承关系如下:

GenericViewSet(viewset) -drf

GenericAPIView -drf

APIView -drf

View  -django

这些view之间的差异就引出了drf中另一个核心点mixin

mixin包括:

CreateModelMixin
    ListModelMixin
    RetrieveModelMixin
    UpdateModelMixin
    DestroyModelMixin


以ListModelMixin为例做区别,如果我们不去继承这个mixin它里面的这些方法的话。 就无法将get 和 list连接起来。无法连接,那么list中所作的所有功能都不能完成。过滤,分页都将享受不到。

  • RetrieveModelMixin对于具体的商品信息进行了获取,序列化。这个在后面的商品详情页会介绍到。
  • UpdateModelMixin中对于部分更新还是全部更新进行了判断。
  • DestroyModelMixin用来连接我们的delete方法,在我们delete时有一些必要的操作,如设置返回状态204等。

GenericAPIView 继承自APIView:新增加了过滤、分页、序列化

GenericAPIView结合各种mixin可以组合成ListAPIView、RetrieveAPIView、等等,新增了get、post等方法


GenericViewSet继承了GenericAPIView 和ViewSetMixin ,ViewSetMixin允许在url配置时进行绑定,例如使用router或者自己进行绑定 用法示例:

model.py

class Goods(models.Model):
    """
    商品
    """
    category = models.ForeignKey(GoodsCategory, verbose_name="商品类目")
    name = models.CharField(max_length=100, verbose_name="商品名")
    sold_num = models.IntegerField(default=0, verbose_name="商品销售量")
    goods_front_image = models.ImageField(upload_to="goods/images/", null=True, blank=True, verbose_name="封面图")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")
    click_num = models.IntegerField(default=0, verbose_name="点击数")

    class Meta:
        verbose_name = '商品'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

采用django的view方法

views.py

第一种写法

但是有很多问题:一个一个取太麻烦,取datetime和imagefield会报错,无法做序列化

import json
from django.http import HttpResponse
class GoodsListView(View):
    def get(self,request):
        json_list=[]
        goods=Goods.objects.all()
        for good in goods:
            json_dict={}
            json_dict["name"]=good.name
            json_dict["category"]=good.category.name
            json_dict["sold_num"]=good.sold_num
            json_list.append(json_dict)


第二种写法

model_to_dict 将model转换为字典,不用一个字段一个字段提取,但还是存在datetime和imagefield无法序列化的问题

from django.forms.models import model_to_dict
import json
from django.http import HttpResponse
class GoodsListView(View):
    def get(self,request):
        json_list=[]
        goods=Goods.objects.all()
        for good in goods:
            json_dict=model_to_dict(good)
            json_list.append(json_dict)
        return HttpResponse(json.dumps(json_list), content_type="application/json")


第三种写法

serializers 专门用于序列化,有了这个序列化,其实上面的model_to_dict都不用做了

import json
from django.core import serializers
from django.http import HttpResponse,JsonResponse
class GoodsListView(View):
    def get(self,request):
        json_data=serializers.serialize("json",goods)
        json_data=json.loads(json_data)
        # 或者return HttpResponse(json.dumps(json_data), content_type="application/json")
        return JsonResponse(json_data,safe=False)

urls.py

url(r'^goods/$',GoodsListView.as_view(),name="good-list"),

drf中采用APIView

新建一个serializers.py

暂且用这两个字段作为示例

from rest_framework import serializers 
class GoodsSerializer(serializers.Serializer): 
    name = serializers.CharField(required=True,max_length=100) 
    click_num = serializers.IntegerField(default=0)


可以修改为以下,这样就可以把所有的字段都进行序列化

class GoodsSerializer(serializers.ModelSerializer):
    class Meta:
        model = Goods
        fields = "__all__"


views.py

class GoodsListView(APIView):
    """
    List all goods     
    """
    def get(self, request, format=None):
        goods = Goods.objects.all()
        goods_serializer = GoodsSerializer(goods, many=True)
        return Response(goods_serializer.data)


drf采用GenericAPIView

views.py

class GoodsListView(mixins.ListModelMixin,generics.GenericAPIView):
    """
    商品列表页
    """
    queryset = Goods.objects.all()
    serializer_class = GoodsSerializer
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)


drf采用GenericViewSet

views.py

class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
    """
    商品列表页
    """
    queryset = Goods.objects.all()
    serializer_class = GoodsSerializer

urls.py

将get请求绑定到list之上,类似于之前的

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


这样我们就不用自己再去绑定了。

配置url时就不用再加as_view()了


goods_list = GoodsListViewSet.as_view({
    'get': 'list',
})
# 商品列表页
url('goods/', goods_list,name="goods-list"),


是我们可以更厉害一点,直接不用进行这个get 与list的绑定,它自动完成。那就是采用router

from goods.views import GoodsListViewSet
from rest_framework.routers import DefaultRouter
router=DefaultRouter()
#配置goods的url
router.register(r'goods', GoodsListViewSet,base_name='goods')

参考文档:https://blog.csdn.net/summer2day/article/details/81367781