GenericAPIView继承自APIView,增加了对于列表视图和详情视图可能用到的通用支持方法。通常使用时,可搭配一个或多个Mixin扩展类。
以下属性控制基本视图行为。
列表视图:获取多条数据(或全部数据),例如:获取所有书籍
详情视图:获取单个数据,例如:获取《西游记》的价格,作者,出版社等信息。
queryset
应用于从此视图返回对象的查询集。通常,您必须设置此属性或覆盖该get_queryset()方法。如果您要覆盖视图方法,请务必调用get_queryset()而不是直接访问此属性。
serializer_class
应该用于验证和反序列化输入以及序列化输出的序列化器类。通常,您必须设置此属性或覆盖该get_serializer_class()方法。
lookup_field
应该用于执行单个模型实例的对象查找的模型字段。默认为’pk’.
lookup_url_kwarg
应该用于对象查找的 URL 关键字参数。URL conf 应包含与此值对应的关键字参数。如果未设置,则默认使用lookup_field.
以下属性用于在与列表视图一起使用时控制分页。
pagination_class
分页列表结果时应使用的分页类。默认为与DEFAULT_PAGINATION_CLASS设置相同的值,即’rest_framework.pagination.PageNumberPagination’。设置pagination_class=None将禁用此视图的分页。filter_backends
应用于过滤查询集的过滤器后端类列表。默认值与DEFAULT_FILTER_BACKENDS设置相同。get_queryset(self)
返回视图使用的查询集,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写,例如:
def get_queryset(self):
return BookInfo.objects.all()
get_serializer_class(self)
返回序列化器类,默认返回serializer_class,可以重写,例如:
def get_serializer_class(self):
return BookInfoModelSerializer
get_serializer(self, _args, *_kwargs)
返回序列化器对象,被其他视图或扩展类使用,如果我们在视图中想要获取序列化器对象,可以直接调用此方法。
get_object(self)
返回详情视图所需的模型类数据对象,默认使用lookup_field参数来过滤queryset。 在视图中可以调用该方法获取详情信息的模型类对象。
若详情访问的模型类对象不存在,会返回404。
经验:GenericAPIView看起来是非常美好的,可以和各个mixin结合起来使用,但是实际上用处不大。因为这样最多是开发速度提高,但是如果产品修改了需求,相应的序列化器就得改。然后有的接口需要A序列化器进行数据检验,有的需要B序列化器,GenericAPIView这时候改起来就很不方便。
建议:使用FVB模式进行开发,这样便于修改,或者是每一个CVB中只有一个方法,但是这样不如直接使用FVB。
下面来举一些例子。这些例子中有些用到了分页器。这并不影响我们的理解。如果不清楚DRF分页器的,可以看这里
列表视图中,每次按照分页大小返回多个数据。如下图所示,我们使用百度搜索的时候,百度会以分页的形式给我们展示数据。
我们的列表视图代码如下:
class PageNum(PageNumberPagination):
"""分页器类"""
page_size = 10 # 默认每页返回的条数
page_query_param = 'pagenum' #url中设置 page 的键,默认为page
page_size_query_param = 'pagesize' # url中设置 page_size 的键,默认为page_size
max_page_size = 50 # 每页返回的最大条数
class BookListView(GenericAPIView):
"""列表视图"""
queryset = BookInfo.objects.all().order_by('id')
serializer_class = BookInfoSerializer
pagination_class = PageNum
def get(self, request):
"""GET请求获取列表"""
data = self.get_queryset() # 获取查询结果集
page = self.paginate_queryset(data) #对查询结果集进行分页
serializer = self.get_serializer(page, many=True) # 序列化
return Response(serializer.data) # 返回值
def post(self, request):
"""post请求新增"""
data = request.data
serializer = self.get_serializer(data=data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=201)
else:
# 返回错误信息
return Response({'msg': '保存失败'}, status=400)
使用GET方法请求,效果如下:
新增成功,返回的响应如下所示:
把列表方法和新增方法放在一个类中的原因是“这两者使用的URL是相同的,而后面的查询一条数据详情,修改一条已有数据,删除一条数据的URL是相同的。”
该视图将包含删除一条数据,修改一条数据以及获取一条数据的详细信息这三个功能,对应的HTTP请求方法分别是DELETE,PUT和GET。它们会使用同样的URL。
class BookView(GenericAPIView):
"""删改查视图"""
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
lookup_field = 'pk'
lookup_url_kwarg = 'pk'
def get(self, request, pk):
"""根据id查询书籍详情"""
obj = self.get_object()
serializers = self.get_serializer(obj)
return Response(serializers.data)
def delete(self, request, pk):
"""根据id删除书籍"""
obj = self.get_object()
obj.delete()
return Response(status=204)
def put(self, request, pk):
"""根据id修改某一本书籍信息"""
obj = self.get_object()
data = request.data
serializers = self.get_serializer(obj, data)
if serializers.is_valid(raise_exception=True):
serializers.save()
# PUT更新,如果返回数据,建议状态码为200;如果不返回数据,状态码根据情况可选择204或者205
return Response(serializers.data, status=200)
else:
return Response(status=400)
我们的URL设计如下所示:
path('book', BookListView.as_view()),
path('book/<int:pk>', BookView.as_view()),
使用http://127.0.0.1:8848/book/3
进行请求,返回的响应如下:
这样就基本体现了RESTFul的设计思路。URL中只涉及名词,接口被统一,使用HTTP请求方法来区分动作。不同的HTTP方法就代表着对资源的不同操作。