DRF框架请求与响应
-
DRF 之Request
Request是包装后的request,前面源码分析过了
- 新包装的Request对象和原来django的request对象没有什么区别,使用方法一样,只是多了
request.data
,当然区别也不止这一点,我们看一下目录
-
request常用方法
- 新包装的Request对象是通过__getattr__方法反射过来的
def __getattr__(self, attr): try: return getattr(self._request, attr) except AttributeError: return self.__getattribute__(attr)
- 现在数据都可以通过request.data获取
@property def data(self): if not _hasattr(self, '_full_data'): self._load_data_and_files() return self._full_data
- 原来request请求提交的数据在GET里,现在请求参数可以通过request.query_parmas来查询参数,也是被伪装成数据属性了,本质还是使用了原来request对象的GET,所以请求参数既可以从GET中取,也可以从request.query_params中取
@property def query_params(self): """ More semantically correct name for request.GET. """ return self._request.GET
- 新包装的Request对象是通过__getattr__方法反射过来的
-
配置请求数据格式
默认的情况下,三种数据格式(urlencoded,formdata,json)都可以解析
比如当请求(post)过来要新增一条数据,那么我们可以针对该请求的数据格式做要求,比如只能提交json格式,或者允许From-data和json等···通过写接口实现,需要进行局部(views)或者全局配置(settings)-
配置处理顺序
- 局部优先
- 全局其次
- 默认配置最后
-
局部配置
- 导入:
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
- 配置参数:parser_classes
配置在视图类里
'''views.py''' # 作者详情视图类 from rest_framework.parsers import JSONParser, FormParser, MultiPartParser class AuthorDetailView(APIView): # 只处理json格式,默认三种格式都可以 # parser_classes = [JSONParser, FormParser, MultiPartParser] parser_classes = [JSONParser] def post(self, request): # 获取反序列化数据 ser = serializer.AuthorDetailSerializer(data=request.data) if ser.is_valid(): # 校验通过存入数据库,不需要重写create方法了 ser.save() return Response({'code': 100, 'msg': '新增成功', 'data': ser.data}) # 校验失败 return Response({'code': 101, 'msg': '校验未通过', 'error': ser.errors})
在局部配置的时候千万注意,如果parser_classes=[],那么什么数据格式都不解析
- 导入:
-
全局配置
- 导入:
from rest_framework import settings
- 源码解释
'''Settings for REST framework are all namespaced in the REST_FRAMEWORK setting. For example your project's `settings.py` file might look like this: REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': [ 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.TemplateHTMLRenderer', ], 'DEFAULT_PARSER_CLASSES': [ 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', 'rest_framework.parsers.MultiPartParser', ], } '''
- 全局settings.py配置
from rest_framework import settings REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES': [ 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', 'rest_framework.parsers.MultiPartParser', ], } '''默认是三种数据格式都解析的,想解析哪种写哪种就行了'''
一般使用默认配置就可以了,如果需要搭配配置,局部和全局也可以一起使用
- 导入:
-
- 新包装的Request对象和原来django的request对象没有什么区别,使用方法一样,只是多了
-
DRf 之 Response
导入:
from rest_framework.response import Response
-
Reponse常用参数
源码
class Response(SimpleTemplateResponse): def __init__(self, data=None, status=None, template_name=None, headers=None, exception=False, content_type=None): ·····
- data:一般处理成字符串,字典,列表等,给http响应body体中的内容
注意,data可以从response对象中取出来二次处理,返回成我想要的样子 Response.data['msg'] = '再加点料'
- status:响应状态码,可以导入,比如:from rest_framework.status import HTTP_201_CREATED,但是建议自定义,不写默认是200
- headers:响应头(字典)
- template_name:模板名称,用浏览器访问需要,默认使用DRF提供的,可以使用自己的
- exception:异常处理
- content_type:响应编码格式
- data:一般处理成字符串,字典,列表等,给http响应body体中的内容
-
局部配置
导入:
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
- JSONRenderer:json格式渲染器
- BrowsableAPIRenderer:浏览API渲染器
通过renderer_classes配置解析格式
'''views.py''' from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer class PublishView(APIView): renderer_classes = [JSONRenderer, ] ····
ps: 如果只配置JSONRenderer,那么浏览器访问也是json格式数据
-
全局配置
settings.py配置
默认两种都解析,取各自所需
REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类 'rest_framework.renderers.JSONRenderer', # json渲染器 'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器 ) }
-
视图组件
-
视图组件大纲
-
两个视图基本类
导入:
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
- APIView:DRF最顶层视图类
- GenericAPIView:DRF通用视图类
-
五个扩展类
扩展类不是视图类,没有继承APIView,需要配合GenericAPIView使用,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法
主要是用来对数据进行增删改查导入
from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin
- CreateModelMixin
- ListModelMixin
- DestroyModelMixin
- RetrieveModelMixin
- UpdateModelMixin
-
九个子类视图
导入
from rest_framework.generics import CreateAPIView,ListAPIView,DestroyAPIView,RetrieveAPIView,UpdateAPIView,ListCreateAPIView,RetrieveUpdateAPIView,RetrieveUpdateDestroyAPIView,RetrieveDestroyAPIView
视图子类其实可以理解为GenericAPIView通用视图类和Mixin扩展类的排列组合组成的,底层事通过封装和继承来写
- CreateAPIView
提供 post 方法 继承自: GenericAPIView、CreateModelMixin
- ListAPIView
提供 get 方法 继承自:GenericAPIView、ListModelMixin
- DestroyAPIView
提供 delete 方法 继承自:GenericAPIView、DestoryModelMixin
- RetrieveAPIView
提供 get 方法 继承自: GenericAPIView、RetrieveModelMixin
- UpdateAPIView
提供 put 和 patch 方法 继承自:GenericAPIView、UpdateModelMixin
- ListCreateAPIView
提供get 和 post方法 继承自:ListModelMixin、CreateModelMixin、GenericAPIView
- RetrieveUpdateAPIView
提供 get、put、patch方法 继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
- RetrieveDestroyAPIView
提供:get、delete方法 继承自:RetrieveModelMixin、DestroyModelMixin、GenericAPIView
- RetrieveUpdateDestroyAPIView
提供 get、put、patch、delete方法 继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
- CreateAPIView
-
视图集
导入
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSet,GenericViewSet,ViewSetMixin
-
常用视图集父类
- ModelViewSet:继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
- ReadOnlyModelViewSet:继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。
- ViewSet:继承自APIView与ViewSetMixin,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典({‘get’:’list’})的映射处理工作。
- 在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
GenericViewSet:使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView,所以还需要继承GenericAPIView。
- 在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
- GenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIView与ViewSetMixin,在实现了调用as_view()时传入字典(如{‘get’:’list’}`)的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。
-
魔法类
- ViewSetMixin:控制自动生成路由
-
-
一览表
-
-
视图组件演变
-
基于APIview的五个接口
class BookView(APIView): def get(self, requets): # 序列化 book_list = models.Book.objects.all() # 序列化多条数据many=True ser = serializer.BookSerializer(instance=book_list, many=True) return Response(ser.data) def post(self, request): # 获取反序列化数据 ser = serializer.BookSerializer(data=request.data) if ser.is_valid(): # 校验通过存入数据库,不需要重写create方法了 ser.save() return Response({'code': 100, 'msg': '新增成功', 'data': ser.data}) # 校验失败 return Response({'code': 101, 'msg': '校验未通过', 'error': ser.errors}) class BookViewDetail(APIView): def get(self, request, pk): book = models.Book.objects.filter(pk=pk).first() ser = serializer.BookSerializer(instance=book) return Response(ser.data) def put(self, request, pk): book = models.Book.objects.filter(pk=pk).first() # 修改,instance和data都要传 ser = serializer.BookSerializer(instance=book, data=request.data) if ser.is_valid(): # 校验通过修改,不需要重写update ser.save() return Response({'code:': 100, 'msg': '修改成功', 'data': ser.data}) # 校验不通过 return Response({'code:': 102, 'msg': '校验未通过,修改失败', 'error': ser.errors}) def delete(self, request, pk): models.Book.objects.filter(pk=pk).delete() return Response({'code': 100, 'msg': '删除成功'})
-
基于GenericAPIView的五个接口
- 常见属性
-GenericAPIView 继承了APIView,封装了一些属性和方法,跟数据库打交道 -queryset = None # 指定序列化集 -serializer_class = None # 指定序列化类 -lookup_field = 'pk' # 查询单条,分组分出来的参数,转换器对象参数的名字 -filter_backends # 过滤排序功能会用它 -pagination_class # 分页功能 -get_queryset() # 获取要序列化的数据,后期可能会重写 -get_object() # 通过lookup_field查询的 -get_serializer() # 使用它序列化 -get_serializer_class() # 返回序列化类 ,后期可能重写 demo: # 指定序列化集 queryset = models.Book.objects.all() # 指定序列化类 serializer_class = serializer.BookSerializer
- 五个接口demo
from rest_framework.response import Response from app01 import models from app01 import serializer from rest_framework.generics import GenericAPIView # 书视图类 class BookView(GenericAPIView): # 指定序列化集 queryset = models.Book.objects.all() # 指定序列化类 serializer_class = serializer.BookSerializer def get(self, requets): # obj = self.queryset() obj = self.get_queryset() # 等同于上面 # ser = self.get_serializer_class()(instance=obj,many=True) ser = self.get_serializer(instance=obj,many=True) # 等同于上面 return Response(ser.data) def post(self, request): # 获取反序列化数据 # ser = serializer.BookSerializer(data=request.data) ser = self.get_serializer(data = request.data) if ser.is_valid(): # 校验通过存入数据库,不需要重写create方法了 ser.save() return Response({'code': 100, 'msg': '新增成功', 'data': ser.data}) # 校验失败 return Response({'code': 101, 'msg': '校验未通过', 'error': ser.errors}) class BookViewDetail(GenericAPIView): # 指定序列化集 queryset = models.Book.objects.all() # 指定序列化类 serializer_class = serializer.BookSerializer def get(self, request, pk): # book = models.Book.objects.filter(pk=pk).first() book = self.get_object() # 根据pk拿到单个对象 # ser = serializer.BookSerializer(instance=book) ser = self.get_serializer(instance=book) return Response(ser.data) def put(self, request, pk): # book = models.Book.objects.filter(pk=pk).first() book = self.get_object() # 修改,instance和data都要传 # ser = serializer.BookSerializer(instance=book, data=request.data) ser = self.get_serializer(instance=book,data=request.data) if ser.is_valid(): # 校验通过修改,不需要重写update ser.save() return Response({'code:': 100, 'msg': '修改成功', 'data': ser.data}) # 校验不通过 return Response({'code:': 102, 'msg': '校验未通过,修改失败', 'error': ser.errors}) def delete(self, request, pk): # models.Book.objects.filter(pk=pk).delete() self.get_object().delete() return Response({'code': 100, 'msg': '删除成功'})
- 路由
path('books/', views.BookView.as_view()), path('books/<int:pk>', views.BookViewDetail.as_view())
- 总结:到第二层只需修改queryset和serializer_class类属性即可,其余都不需要修改
- 注意:虽然pk没有在orm语句中过滤使用,但是路由分组要用,所以不能删,或者写成*args **kwargs接收多余的参数,且路由转换器必须写成pk
# 源码 lookup_field = 'pk' lookup_url_kwarg = None # get_queryset()方法可以重写,如果我们需要在一个视图类内操作另外表模型,来指定序列化的数据 class BookViewDetail(GenericAPIView): queryset = models.Book.objects.all() ··· ''' 指定序列化数据的格式: self.queryset() self.get_queryset() # 等同于上面 queryset = models.Book.objects.all() ''' # 可以重写get_queryset方法在book视图类里操作作者模型 def get_queryset(self,request): if self.request.path == '/user' return Author.objects.all() ··· # 这样序列化的数据就不一样了,根据不同的条件序列化不同的数据 '''当然还可以通过重写get_serializer_class来返回其他序列化器类'''
- 常见属性
-
基于GenericAPIView+五个视图扩展类写
五个视图扩展类:
from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin
通过GenericAPIView+视图扩展类来使得代码更简单,一个接口对应一个扩展类,注意扩展类不是视图类
- ListModelMixin:获取所有API,对应list()方法
- CreateModelMixin:新增一条API,对应create()方法
- UpdateModelMixin:修改一条API,对应update()方法
- RetrieveModelMixin:获取一条API,对应retrieve()方法
- DestroyModelMixin:删除一条API,对应destroy()方法
- 注意:CreateModelMixin扩展类提供了更高级的方法,可以通过重写来校验数据存入
def perform_create(self, serializer): serializer.save()
- 五个接口的demo
from app01 import models from app01 import serializer from rest_framework.generics import GenericAPIView from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin # 获取所有和新增API class BookView(ListModelMixin,CreateModelMixin,GenericAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer def get(self, request): return super().list(request) def post(self, request): return super().create(request) # 获取删除修改单个API class BookViewDetail(UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin,GenericAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer def get(self, request, *args,**kwargs): return super().retrieve(request, *args,**kwargs) def put(self, request, *args,**kwargs): return super().update(request, *args,**kwargs) def delete(self, request, *args,**kwargs): return super().destroy(request, *args,**kwargs)
-
GenericAPIView速写五个接口demo
- 模型
from django.db import models # Create your models here. # build four model tables class Book(models.Model): name = models.CharField(max_length=32) price = models.DecimalField(decimal_places=2, max_digits=5) publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE) authors = models.ManyToManyField(to='Author') def __str__(self): return self.name # 自定制字段 @property def publish_detail(self): return {'name': self.publish.name, 'addr': self.publish.city} @property def author_list(self): l = [] print(self.authors.all()) # <QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]> for author in self.authors.all(): print(author.author_detail) # AuthorDetail object (1) l.append({'name': author.name, 'age': author.age, 'addr': author.author_detail.addr}) return l class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE) def __str__(self): return self.name @property def authordetail_info(self): return {'phone':self.author_detail.telephone,'addr':self.author_detail.addr} class AuthorDetail(models.Model): telephone = models.BigIntegerField() addr = models.CharField(max_length=64) class Publish(models.Model): name = models.CharField(max_length=32) city = models.CharField(max_length=32) email = models.EmailField()
- 序列化器
from django.db import models # Create your models here. # build four model tables class Book(models.Model): name = models.CharField(max_length=32) price = models.DecimalField(decimal_places=2, max_digits=5) publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE) authors = models.ManyToManyField(to='Author') def __str__(self): return self.name # 自定制字段 @property def publish_detail(self): return {'name': self.publish.name, 'addr': self.publish.city} @property def author_list(self): l = [] print(self.authors.all()) # <QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]> for author in self.authors.all(): print(author.author_detail) # AuthorDetail object (1) l.append({'name': author.name, 'age': author.age, 'addr': author.author_detail.addr}) return l class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE) def __str__(self): return self.name @property def authordetail_info(self): return {'phone':self.author_detail.telephone,'addr':self.author_detail.addr} class AuthorDetail(models.Model): telephone = models.BigIntegerField() addr = models.CharField(max_length=64) class Publish(models.Model): name = models.CharField(max_length=32) city = models.CharField(max_length=32) email = models.EmailField()
- 视图
from rest_framework.generics import GenericAPIView from rest_framework.mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin, RetrieveModelMixin, \ UpdateModelMixin from app01 import models from app01 import serializer # 书视图类 class BookView(ListModelMixin, CreateModelMixin, GenericAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer def get(self, request): return super().list(request) def post(self, request): return super().create(request) class BookViewDetail(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer def get(self, request, *args, **kwargs): return super().retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return super().update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return super().destroy(request, *args, **kwargs) # 作者 class AuthorView(ListModelMixin, CreateModelMixin, GenericAPIView): queryset = models.Author.objects.all() serializer_class = serializer.AuthorSerializer def get(self, request): return super().list(request) def post(self, request): return super().create(request) class AuthorViewDetail(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView): queryset = models.Author.objects.all() serializer_class = serializer.AuthorSerializer def get(self, request, *args, **kwargs): return super().retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return super().update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return super().destroy(request, *args, **kwargs) # 作者详情 class AuthorDetailView(ListModelMixin, CreateModelMixin, GenericAPIView): queryset = models.AuthorDetail.objects.all() serializer_class = serializer.AuthorDetailSerializer def get(self, request): return super().list(request) def post(self, request): return super().create(request) class OneAuthorViewDetail(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView): queryset = models.AuthorDetail.objects.all() serializer_class = serializer.AuthorDetailSerializer def get(self, request, *args, **kwargs): return super().retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return super().update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return super().destroy(request, *args, **kwargs) # 出版社 class PublishView(ListModelMixin, CreateModelMixin, GenericAPIView): queryset = models.Publish.objects.all() serializer_class = serializer.PublishSerializer def get(self, request): return super().list(request) def post(self, request): return super().create(request) class PublishViewDetail(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView): queryset = models.Publish.objects.all() serializer_class = serializer.PublishSerializer def get(self, request, *args, **kwargs): return super().retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return super().update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return super().destroy(request, *args, **kwargs)
- 路由
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), # 书 path('books/', views.BookView.as_view()), path('books/<int:pk>', views.BookViewDetail.as_view()), # 作者 path('authors/', views.AuthorView.as_view()), path('authors/<int:pk>', views.AuthorViewDetail.as_view()), # 作者详情 path('authorsdetail/', views.AuthorDetailView.as_view()), path('authorsdetail/<int:pk>', views.OneAuthorViewDetail.as_view()), # 出版社 path('publish/', views.PublishView.as_view()), path('publish/<int:pk>', views.PublishViewDetail.as_view()), ]
- 模型
-
基于GenericAPIView+九个视图子类写五个接口
使用哪个继承哪个就可以了,具体可以看继承的父类里有什么方法不需要刻意去记
from rest_framework.generics import CreateAPIView,ListAPIView,DestroyAPIView,RetrieveAPIView,UpdateAPIView,ListCreateAPIView,RetrieveUpdateAPIView,RetrieveUpdateDestroyAPIView,RetrieveDestroyAPIView # 1、查询所有,新增API class BookView(ListCreateAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer # 2、新增接口 class BookView(CreateAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer # 3、查询接口 class BookView(ListAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer # 4、查询单个,修改一个,删除一个接口 class BookViewDetail(RetrieveUpdateDestroyAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer # 5、查询单个接口 class BookViewDetail(RetrieveAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer # 6、修改单个接口 class BookViewDetail(UpdateAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer # 7、删除单个接口 class BookViewDetail(DestroyAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer # 8、查询单个、修改接口 class BookViewDetail(RetrieveUpdateAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer # 9、查询单个、删除接口 class BookViewDetail(RetrieveDestroyAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer '''上述共九个视图子类,九九归一剑诀~''' # 更新和删除接口自己整合 class BookViewDetail(UpdateAPIView,DestroyAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer
- 五接口速写
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView from app01 import models from app01 import serializer class BookView(ListCreateAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer class BookViewDetail(RetrieveUpdateDestroyAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer '''其余的和第三层一样'''
- 五接口速写
-
基于ViewSet写五个接口
-
路由
-
导入:
from rest_framework.routers import SimpleRouter,DefaultRouter
-
方法一:
from django.urls import path, include from rest_framework.routers import SimpleRouter from app01 import views router = SimpleRouter() router.register('books', views.BookView, 'books') urlpatterns = [ ... ] urlpatterns += router.urls ''' register(self, prefix, viewset, basename=None) prefix:路由url前缀 viewset:处理请求的viewset类 basename:路由名称的前缀,一般和prefix写成一样就行 ''' # 等同于 path('books/'),include(router.urls) path('books/<int:pk>'),include(router.urls)
-
方法二:
router = SimpleRouter() router.register('books', views.BookView, 'books') urlpatterns = [ ... url(r'^', include(router.urls)) ] # 生成两种路由 path('/api/v1'),include(router.urls) # [<URLPattern '^books/$' [name='books-list']>, <URLPattern '^books/(?P<pk>[^/.]+)/$' [name='books-detail']>] # 等同于自己配的 path('/api/v1/books/'),include(router.urls) path('/api/v1/books/<int:pk>'),include(router.urls)
-
异同:
同:方法一和方法二都可以自动生成路由,代替了下面的路由
path('books/', views.BookView.as_view()), path('books/<int:pk>', views.BookViewDetail.as_view()),
异:方法二可以拼接路径,如果不拼接是和方法一一样的
-
-
视图集
- 导入:
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSet,GenericViewSet,ViewSetMixin
- views.py
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSet,GenericViewSet,ViewSetMixin class BookView(ModelViewSet): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer
- urls.py
from django.contrib import admin from django.urls import path, include from rest_framework.routers import SimpleRouter from app01 import views router = SimpleRouter() router.register('books', views.BookView, 'books') urlpatterns = [ path('admin/', admin.site.urls), path('api/v1/',include(router.urls)), ]
- ReadOnlyModelViewSet视图集
继承该ReadOnlyModelViewSet视图集的作用是只读,只做查询,修改删除等操作不允许
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSet,GenericViewSet,ViewSetMixin class BookView(ReadOnlyModelViewSet): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer
- ModelViewSet与ReadOnlyModelViewSet视图集de总结
ModelViewSet可以写五个接口,而ReadOnlyModelViewSet只能写两个接口
- 本质
-
ModelViewSet继承了五个视图扩展类+GenericViewSet,GenericViewSet继承了ViewSetMixin+GenericAPIView
PS:ViewSetMixin控制了路由写法
-
ReadOnlyModelViewSet继承了RetrieveModelMixin+ListModelMixin+GenericViewSet
-
- 本质
- 其他视图集
- ViewSet
ViewSet = ViewSetMixin+APIView
class ViewSet(ViewSetMixin, views.APIView): """ The base ViewSet class does not provide any actions by default. """ pass
- GenericViewSet
GenericViewSet = ViewSetMixin+GenericAPIView
class GenericViewSet(ViewSetMixin, generics.GenericAPIView): """ The GenericViewSet class does not provide any actions by default, but does include the base set of generic view behavior, such as the `get_object` and `get_queryset` methods. """ pass
- ViewSetMixin
魔术视图类,控制自动生成路由,可以通过组合继承,以前的写法可以继续使用,但是如果要自动生成路由必须得继承ViewSetMixin及其子类;或者选择继承ViewSet、GenericViewSet
class ViewSetMixin: """ This is the magic. Overrides `.as_view()` so that it takes an `actions` keyword that performs the binding of HTTP methods to actions on the Resource. For example, to create a concrete view binding the 'GET' and 'POST' methods to the 'list' and 'create' actions... view = MyViewSet.as_view({'get': 'list', 'post': 'create'}) """ 这就是魔法。 重写' .as_view() ',以便它接受一个' actions '关键字执行 将HTTP方法绑定到资源上的动作。 例如,创建绑定'GET'和'POST'方法的具体视图 到“列表”和“创建”动作… = MyViewSet视图。 As_view ({'get': 'list', 'post': 'create'})
- ViewSet
- 导入:
-
-
总结
- 第一层:基于APIView写视图,get、post、put、delete都需要自己写,序列化的数据和序列化类需要获取后指定
class BookView(APIView): def get(self, requets): book_list = models.Book.objects.all() ser = serializer.BookSerializer(instance=book_list, many=True) return Response(ser.data)
- 第二层:基于GenericAPIView写视图,优化了视图类内序列化数据和序列化类的代码冗余问题,通过queryset和serializer_class指定序列化集和序列化器即可,一个视图类内写一次即可,最后通过get_queryset和get_serializer方法处理
class BookView(GenericAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer def get(self, requets): obj = self.get_queryset() ser = self.get_serializer(instance=obj,many=True) return Response(ser.data)
- 第三层:基于GenericAPIView+5个视图扩展类写视图,每个扩展类对应一个接口,更加细化,通过继承父类(扩展类)减少了代码的冗余
class BookView(ListModelMixin,CreateModelMixin,GenericAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer def get(self, request): return super().list(request)
- 第四层,基于九个视图子类写,视图子类将扩展类和GenericAPIView封装到一块,使得我们要写的代码更少了,总之就是牛逼~
class BookView(ListCreateAPIView): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer
- 第五层,基于ViewSet写视图,这样以来5个接口就都在一个视图类内,代码更少了,但是可扩展性低了,路由也是问题,get所有和get一条路由冲突需要修改
class BookView(ModelViewSet): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer '''路由''' router = SimpleRouter() router.register('books', views.BookView, 'books') urlpatterns = [ path('admin/', admin.site.urls), path('api/v1/',include(router.urls)), ]
- 第一层:基于APIView写视图,get、post、put、delete都需要自己写,序列化的数据和序列化类需要获取后指定
-
视图集中定义附加action动作
- 视图
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet class StudentModelViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer def login(self,request): """学生登录功能""" return Response({"message":"登录成功"})
- 路由
urlpatterns = [ path("students8/", views.StudentModelViewSet.as_view({"get": "list", "post": "create"})), re_path("students8/(?P<pk>\d+)/", views.StudentModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})), path("stu/login/",views.StudentModelViewSet.as_view({"get":"login"})) ]
- action属性
from rest_framework.viewsets import ModelViewSet from students.models import Student from .serializers import StudentModelSerializer from rest_framework.response import Response class StudentModelViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer def get_new_5(self,request): """获取最近添加的5个学生信息""" # 操作数据库 print(self.action) # 获取本次请求的视图方法名 通过路由访问到当前方法中.可以看到本次的action就是请求的方法名
- 视图
-
路由组件
REST framework提供了两个router
- SimpleRouter
- DefaultRouter
路由组件使用,如果视图类继承了ViewSetMixin及其子类,那么路由写法可以改变,而且视图类中的方法也可以自定制,不一定是get,post,可以随意命名
from rest_framework.views import APIView from rest_framework.viewsets import ViewSetMixin from rest_framework.response import Response class TestView(ViewSetMixin,APIView): def login(self,requets): return Response('test-login') # 路由 path('test/',views.TestView.as_view({'get': 'login'}))
注意
# 继承的时候,如果继承了ModelViewSet就相当于继承了ViewSetMixin的子类了,不需要继承ViewSetMixin了,不然会报错 class BookView(ModelViewSet): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer path('books/', views.BookView.as_view({'get':'list'})),
-
路由是如何映射的?
1、ViewSetMixin写在前面 2、先走ViewSetMixin的as_view if not actions: raise TypeError("The `actions` argument must be provided when " "calling `.as_view()` on a ViewSet. For example " "`.as_view({'get': 'list'})`") 4、如果actions不传就报错,也就是as_view()内不写字典就报错 5、view闭包函数 def view(request, *args, **kwargs): self = cls(**initkwargs) ···· for method, action in actions.items(): '''method:get action:login''' # 把login方法的内存地址给了handler handler = getattr(self, action) # 通过反射,设置给get---》对应login---》get请求执行get方法,现在get方法变成了login方法 setattr(self, method, handler) return self.dispatch(request, *args, **kwargs)# 跟之前一样了
-
继承ModelViewSet,路由写法
ModelViewSet继承了五个扩展类+GenericViewSet,提供了相应的接口方法增删改查
from rest_framework.viewsets import ModelViewSet class BookView(ModelViewSet): queryset = models.Book.objects.all() serializer_class = serializer.BookSerializer
- 自己配路由的映射
path('books/', views.BookView.as_view({'get':'list','post':'create'})), path('books/<int:pk>', views.BookView.as_view({'get':'retrieve','put':'update','delete':'destroy'}))
- 自动生成路由
from rest_framework.routers import SimpleRouter from app01 import views router = SimpleRouter() router.register('books', views.BookView, 'books') urlpatterns = [ path('api/v1/', include(router.urls)), ] ''' 或者不写path('api/v1/', include(router.urls)),写下面的 urlpatterns += router.urls '''
- 自己配路由的映射
action装饰器
只要是继承ViewSetMixin视图类或其子类,都可以加action装饰器
- 导入:rest_framework.decorators.action
- 参数:
- methods:请求方法,列表的形式写
- detail:是否带id,True不带id,False带id
- url_path:地址,地址如果不写,默认已方法名为地址
- url_name:起别名
- 继承APIView+ViewSetMixin使用装饰器
from rest_framework.decorators import action class TestView(ViewSetMixin,APIView): @action(methods=['GET','POST'],detail=False) def login(self,requets): return Response('test-login')
from rest_framework.routers import SimpleRouter from app01 import views router1 = SimpleRouter() router1.register('test',views.TestView,'test') urlpatterns = [ path('aip/v2/', include(router1.urls)), ] ''' http://127.0.0.1:8000/aip/v2/test/login/ '''
- 注意
如果这样写detail=True,路由就成了http://127.0.0.1:8000/aip/v2/test/1/login/,数字的部分一般为pk 最后都是路由前缀拼接一个方法名
-
路由router形成URL的方式
-
总结
- 路由的写法有三种:
- 手动配置:
path('books/<int:pk>', views.BookDetailView.as_view())
- 继承ViewSetMixin,使用action属性:
views.PublishView.as_view({'get':'list','post':'create'}))
- 使用SimpleRouter或DefaultRouter自动生成(两种方式):
router = SimpleRouter() router.register('books', views.PublishView, 'books') urlpatterns = [ path('api/v1/', include(router.urls)), ] # 或者 urlpatterns = [ urlpatterns+=router.urls ]
- DefaultRouter比SimpleRouter多一层根路径
- 手动配置:
- 路由的写法有三种:
-
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/144006.html