drf之视图层
视图组件和视图类的封装过程
视图组件介绍
APIView跟View区别
与View相比
1.传入到视图方法中的是REST_framework的Request对象,而不是Django的HttpRequeset对象;
2.视图方法可以返回REST_framework的Response对象
3.任何APIException异常都会被捕获到,并且处理成合适的响应信息
4.在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制
APIVIew
类属性:
renderer_classes # 响应格式
parser_classes # 能够解析的请求格式
authentication_classes # 认证类
throttle_classes # 频率类
permission_classes # 权限类
基于APIView+ModelSerializer+Resposne写5个接口
2个视图基类、5个视图扩展类以及9个视图子类
两个视图基类
APIView 和GenericAPIView
五个视图扩展类
CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin,ListModelMixin
九个视图子类
ListAPIView, CreateAPIView, ListCreateAPIView,RetrieveAPIView, UpdateAPIView, DestroyAPIView, RetrieveDestroyAPIView, RetrieveUpdateAPIView, RetrieveUpdateDestroyAPIView
视图类,不需要额外继承GenericAPIView,只需要继承9个中其中某个,就会有某个或某几个接口
两个视图基类
基于APIView+ModelSerializer+Resposne写5个接口
路由
urlpatterns = [
path('admin/', admin.site.urls),
path('books/',views.BookAPIView.as_view()),
path('books/<int:pk>/',views.BookDetailView.as_view())
]
视图类
from rest_framework.views import APIView, Response
from .models import Book
from .serializer import BookSerializer
class BookAPIView(APIView):
# 获取序列化多条
def get(self, request):
books = Book.objects.all()
ser = BookSerializer(instance=books, many=True)
return Response(ser.data)
# 新增图书
def post(self, request):
ser = BookSerializer(data=request.data)
if ser.is_valid():
ser.save() # 书写create方法
"""1.想要的对象----ser序列化类的对象
2.新增的对象----想要序列化成字典---》前提是序列化类中的create方法一定要返回新增的对象"""
return Response({'code': 100, 'msg': '新增成功','result': ser.data}})
else:
return Response({'code': 100, 'msg': ser.errors})
class BookDetailView(APIView):
# 获取单条
def get(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book)
return Response(ser.data)
# 修改图书
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(data=request.data, instance=book)
if ser.is_valid():
ser.save() # 书写update方法
return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
else:
return Response({'code': 100, 'msg': ser.errors})
# 删除
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({'code': 100, 'msg': '删除成功'})
序列化类
# ModelSerializer的使用
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book # 与book表关联
fields = ['name', 'price', 'get_publish_detail', 'get_author_list', 'publish', 'authors'] # 序列化字段
# 定制字段
extra_kwargs = {
'name': {'max_length': 8},
'publish_detail': {'read_only': True},
'author_list': {'read_only': True},
'publish': {'write_only': True},
'authors': {'write_only': True}
}
继承GenericAPIView,编写五个接口
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
class BookAPIView(GenericAPIView):
queryset = Book.objects.all() #
serializer_class = BookSerializer
def get(self, request):
# objs = Book.objects.all()
# objs = self.queryset # 可以拿到,但是不建议这么用,GenericAPIView提供了一个方法
objs = self.get_queryset()
"""
1.get_queryset()该方法的好处就是可以重写get_queryset()方法,后期扩展性比较好
2.去序列化类,不要使用self.serializer_class ,而是使用get_serializer_class
"""
# ser = BookSerializer(instance=objs, many=True)
ser=self.get_serializer(instance=objs,many=True)
return Response(ser.data)
def post(self, request):
ser = self.get_serializer(data=request.data)
if ser.is_valid():
ser.save() # 书写create方法
return Response({'code': 100, 'msg': '新增成功'})
else:
return Response({'code': 100, 'msg': ser.errors})
class BookDetailView(GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self, request, pk):
obj=self.get_object() # 获取单条
ser = self.get_serializer(instance=obj)
return Response(ser.data)
def put(self, request, pk):
obj=self.get_object()
ser = self.get_serializer(data=request.data, instance=obj)
if ser.is_valid():
ser.save() # 书写update方法
return Response({'code': 100, 'msg': '修改成功'})
else:
return Response({'code': 100, 'msg': ser.errors})
def delete(self, request, pk):
self.get_object().delete()
return Response({'code': 100, 'msg': '删除成功'})
五个视图扩展类
基于GenericAPIView + 五个视图扩展类
- GenericAPIView的属性和方法
from rest_framework.generics import GenericAPIView
# 属性
1 queryset:要序列化或反序列化的表模型数据
2 serializer_class:使用的序列化类
3 lookup_field :查询单条的路由分组分出来的字段名
4 filter_backends:过滤类的配置(了解)
5 pagination_class:分页类的配置(了解)
# 方法
1 get_queryset :获取要序列化的对象
2 get_object :获取单个对象
3 get_serializer :获取序列化类 ,跟它差不多的get_serializer_class,一般重写它,不调用它
4 filter_queryset :过滤有关系(了解)
- 基于GenericAPIView + 五个视图扩展类
5个视图扩展类+GenericAPIView= 视图类
路由
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookView.as_view()),
path('books/<int:pk>/', views.BookDetailView.as_view()),
]
序列化类
# ModelSerializer的使用
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book # 与book表关联
fields = ['name', 'price', 'get_publish_detail', 'get_author_list', 'publish', 'authors'] # 序列化字段
# 定制字段
extra_kwargs = {
'name': {'max_length': 8},
'publish_detail': {'read_only': True},
'author_list': {'read_only': True},
'publish': {'write_only': True},
'authors': {'write_only': True}
}
视图类
from rest_framework.mixins import CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin,ListModelMixin
class BookAPIView(GenericAPIView,ListModelMixin,CreateModelMixin):
queryset = Book.objects.all() #
serializer_class = BookSerializer
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
class BookDetailView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
# def get(self, request, pk):
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)
九个视图子类
封装成九个视图子类
这九个是视图子类,不需要额外继承GenericAPIView,只需要继承9个中其中某个,就会有某个或某几个接口,可以点击ListAPIView源码分析可知,该视图类有一个get方法,返回是一个list;该类继承的是GenericAPIView,父类有的方法,子类都可以使用,所以不再需要额外继承GenericAPIView


""" 基于上面再封装成九个视图类"""
from rest_framework.generics import ListAPIView, CreateAPIView, ListCreateAPIView
from rest_framework.generics import RetrieveAPIView, UpdateAPIView, DestroyAPIView, RetrieveDestroyAPIView, \
RetrieveUpdateAPIView, RetrieveUpdateDestroyAPIView # 没有DestroyUpdateAPIView方法,前提是没有查询删除不了
# 查询图书所有
class BookListView(ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 图书新增
class BookCreateView(CreateAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 图书修改
class BookUpdateView(UpdateAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 查询图书单条
class BookRetrieveView(RetrieveAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 删除图书
class BookDestroyView(DestroyAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 图书查询和删除功能
class BookRetrieveDestroyView(RetrieveDestroyAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 图书查询和修改功能
class BookRetrieveUpdateView(RetrieveUpdateAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 图书查询、修改和删除
# 查询图书单条
class BookRetrieveUpdateDestroyView(RetrieveUpdateDestroyAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
""" 总结:
ListAPIView + CreateAPIView = ListCreateAPIView 给BookView继承的
RetrieveAPIView + DestroyAPIView = RetrieveDestroyAPIView 给BookDetailView视图提供的
RetrieveAPIView + UpdateAPIView = RetrieveDestroyAPIView 给BookDetailView视图提供的
RetrieveAPIView + UpdateAPIView + DestroyAPIView = RetrieveUpdateDestroyAPIView 给BookDetailView视图提供的
发现Destroy 和Update 方法没有整合,
"""
最终通过继承可以写成下面两个类
""" 将上述的功能再整合一起"""
# 路由
urlpatterns = [
path('books/', views.BookView.as_view()),
path('books/<int:pk>/', views.BookView.as_view()),
]
# 视图
class BookAPIView(ListCreateAPIView):# 查询所有和新增一个
queryset = Book.objects.all()
serializer_class = BookSerializer
class BookDetailView(RetrieveUpdateDestroyAPIView): # 查询单条、更新和删除
queryset = Book.objects.all()
serializer_class = BookSerializer
视图类的封装过程
1. 基于APIView编写五个接口
class BookAPIView(APIView):
def get(self, request):
books = Book.objects.all()
ser = BookSerializer(instance=books, many=True)
return Response(ser.data)
def post(self, request):
ser = BookSerializer(data=request.data)
if ser.is_valid():
ser.save() # 书写create方法
return Response({'code': 100, 'msg': '新增成功'})
else:
return Response({'code': 100, 'msg': ser.errors})
class BookDetailView(APIView):
def get(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book)
return Response(ser.data)
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(data=request.data, instance=book)
if ser.is_valid():
ser.save() # 书写update方法
return Response({'code': 100, 'msg': '修改成功'})
else:
return Response({'code': 100, 'msg': ser.errors})
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({'code': 100, 'msg': '删除成功'})
2. 继承了GenericAPIView,编写五个接口
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
class BookAPIView(GenericAPIView):
queryset = Book.objects.all() #
serializer_class = BookSerializer
def get(self, request):
# objs = Book.objects.all()
# objs = self.queryset # 可以拿到,但是不建议这么用,GenericAPIView提供了一个方法
objs = self.get_queryset()
"""
1.get_queryset()该方法的好处就是可以重写get_queryset()方法,后期扩展性比较好
2.去序列化类,不要使用self.serializer_class ,而是使用get_serializer_class
"""
# ser = BookSerializer(instance=objs, many=True)
ser=self.get_serializer(instance=objs,many=True)
return Response(ser.data)
def post(self, request):
ser = self.get_serializer(data=request.data)
if ser.is_valid():
ser.save() # 书写create方法
return Response({'code': 100, 'msg': '新增成功'})
else:
return Response({'code': 100, 'msg': ser.errors})
class BookDetailView(GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self, request, pk):
obj=self.get_object() # 获取单条
ser = self.get_serializer(instance=obj)
return Response(ser.data)
def put(self, request, pk):
obj=self.get_object()
ser = self.get_serializer(data=request.data, instance=obj)
if ser.is_valid():
ser.save() # 书写update方法
return Response({'code': 100, 'msg': '修改成功'})
else:
return Response({'code': 100, 'msg': ser.errors})
def delete(self, request, pk):
self.get_object().delete()
return Response({'code': 100, 'msg': '删除成功'})
3. 基于GenericAPIView +五个视图扩展类 编写五个接口
""" 基于GenericAPIView+5个视图扩展类写接口
都继承GenericAPIView,Publish和Book的区别是两个类属性
在基础上封装5个视图扩展类编写5个接口,五个视图扩展类必须要配合GenericAPIView一起用才可以因为没有继承APIView及其子类
CreateModelMixin:新增,之前封装写的是post方法--->而CreateModelMixin写了create方法,代码就是post的代码
DestroyModelMixin:删除 destroy方法
RetrieveModelMixin:查单条数据 retrieve方法
ListModelMixin:查所有数据对象 list 方法
UpdateModelMixin:更新 update方法
"""
from rest_framework.mixins import CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin,ListModelMixin
class BookAPIView(GenericAPIView,ListModelMixin,CreateModelMixin):
queryset = Book.objects.all() #
serializer_class = BookSerializer
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
class BookDetailView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
# def get(self, request, pk):
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)
4. 基于上述再封装9个视图类---->整合两个视图类
""" 基于上面再封装成九个视图类"""
from rest_framework.generics import ListAPIView, CreateAPIView, ListCreateAPIView
from rest_framework.generics import RetrieveAPIView, UpdateAPIView, DestroyAPIView, RetrieveDestroyAPIView, \
RetrieveUpdateAPIView, RetrieveUpdateDestroyAPIView # 没有DestroyUpdateAPIView方法,前提是没有查询删除不了
```python
# 路由
urlpatterns = [
path('books/', views.BookView.as_view()),
path('books/<int:pk>/', views.BookView.as_view()),
]
# 视图
class BookAPIView(ListCreateAPIView):# 查询所有和新增一个
queryset = Book.objects.all()
serializer_class = BookSerializer
class BookDetailView(RetrieveUpdateDestroyAPIView): # 查询单条、更新和删除
queryset = Book.objects.all()
serializer_class = BookSerializer
5. 基于ModelViewSet继承,编写五个类
要编写五个接口,对应写两个视图类,配置两条路由,通过观察发现连个视图类的代码几乎一模一样,我们借此引入了ModelViewSet类,继承它只需要编写一个视图类
改变了路由写法--->
path('books/', views.BookAPIView.as_view({'get': 'list', 'post': 'create'})), path('books/<int:pk>/',views.BookAPIView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
在路由里面指名两个get方法分别对应的类具体方法,当是get请求,访问这个地址,就执行视图类的list方法或retrieve方法
# 视图类
from rest_framework.viewsets import ModelViewSet
class BookAPIView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 路由
path('books/', views.BookAPIView.as_view({'get': 'list', 'post': 'create'})),
path('books/<int:pk>/', views.BookAPIView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
解析源码
点开ModelViewSet源码可知,ModelViewSet封装了ListAPIView、 CreateAPIView、RetrieveAPIView、UpdateAPIView、DestroyAPIView,直接继承ModelViewSet可以直接使用create、list、retrieve、destroy和update五种方法,但是我们查询单表和多条都是get的方法,所以需要在路由指名两个路由的get方法分别对应内置的方法( path('books/', views.BookAPIView.as_view({'get': 'list', 'post': 'create'})))


视图集
1.通过ModelView编写5个接口
# 视图类
from rest_framework.viewsets import ModelViewSet
class BookAPIView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 路由
path('books/', views.BookAPIView.as_view({'get': 'list', 'post': 'create'})),
path('books/<int:pk>/', views.BookAPIView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
2.通过ReadOnlyModelViewSet编写2个只读接口
ReadOnlyModelViewSet,该视图类只有只读方法,继承该方法,路由需要指名get对应的是查询所有还是查询单个,点开源码可知:

# 路由
urlpatterns = [
path('books/', views.BookView.as_view({'get': 'list'})),
path('books/<int:pk>/', views.BookView.as_view({'get': 'retrieve'})),
]
# 视图类
class BookView(ReadOnlyModelViewSet): # 查询所有,新增一个
queryset = Book.objects.all()
serializer_class = BookSerializer
3.ViewSetMixin源码分析


源码分析:
ModelViewSet=CreateModelMixin+RetrieveModelMixin+UpdateModelMixin+DestroyModelMixin+ListModelMixin+GenericViewSet(没见过)
GenericViewSet=ViewSetMixin【没见过】+GenericAPIView【见过,两个视图基类的】
由上述可知,路由写法改变的原因源自ViewSetMixin

ViewSetMixin由注释可知,继承该类,路由的写法要遵循它要求的编写,路由配置就view.MyViewSet.as_view({'get': 'list', 'post': 'create'})
ViewSetMixin源码分析
1.当请求来了,路由匹配成功--->get请求,就会匹配成功books,就会执行views.BookView.as_view({'get': 'list', 'post': 'create'})()--->当执行as_view时,注意此时的as_view是ViewSetMixin的as_view
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
# 如果没有传actions,直接抛异常,路由写法变了后,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'})`")
# 。。。。其他代码不用看
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if 'get' in actions and 'head' not in actions:
actions['head'] = actions['get']
self.action_map = actions
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)
return self.dispatch(request, *args, **kwargs)
# 去除了csrf校验
return csrf_exempt(view)
2.路由匹配成功执行 views.BookView.as_view({'get': 'list', 'post': 'create'})()--->它的本质是执行:ViewSetMixin中的as_view内的view()
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if 'get' in actions and 'head' not in actions:
actions['head'] = actions['get']
# actions是传入的字典--->{'get':'list','post':'create'}
self.action_map = actions
# 第一次循环:method:get action :list
# 第一次循环:method:post action : create
for method, action in actions.items():
# 反射:去视图类中反射 action对应的方法,action第一次是list,去视图集反射list方法
# handler 就是视图类中的list方法
handler = getattr(self, action)
# 反射修改:把method:get 请求方法,handler:list
# 将视图类的对象get方法,变成了list方法
setattr(self, method, handler)
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs) # dispatch 是APIView的方法
总结
- 只要是继承
ViewSetMixin的视图类,路由写法就变了(重写了as_view)
路由变成了需要传入字典映射方法:{'get':'list','post':'create'} - 只要传入
actions,以后访问get就是访问list,访问post就是访问create - 其他执行跟之前一样
- 以后视图类类中的方法名,可以任意命名,只要在路由中做好映射即可(重要)
- ViewSetMixin:不是视图类,y要配合视图类一起,它重写了
as_view方法,导致路由写法变了,路由改变的本质就是通过传的actions做映射{'get':"xxx"} - 继承
APIView+自动生成路由---->ViewSet
class BookView(ViewSet,APIView): # ViewSet一定要写在前面
pass
- 继承GenericAPIView+自动生成路由---->GenericViewSet
class BookView(GenericViewSet,GenericAPIView): # GenericViewSet一定要写在前面
pass
4.基于ViewSetMixin+APIView写视图类
由此可知,继承了ViewSetMixin,可扩展性就高了,可以通过指定方法,编写想要的方法,如下:通过将get=login,当请求来了,就优先执行视图类中的login方法
from rest_framework.viewsets import ViewSet,GenericViewSet,ViewSetMixin
class TestVAPIView(ViewSetMixin,APIView):
def login(self,request):
return Response('登录功能')
from rest_framework.viewsets包下的类
from rest_framework.viewsets下有几个类
ModelViewSet: 5个视图扩展类+ViewSetMixin+GenericAPIView
ReadOnlyModelViewSet: 2个视图扩展类+ ViewSetMixin + GenericAPIView 只读
ViewSetMixin: 魔法方法,该类重写as_view,继承它,路由写法变成了映射方法
ViewSet: ViewSetMixin + APIView
GenericViewSet :ViewSetMixin + GeneriCAPIView
注意:
- 以后想继承
APIView,但想改变路由写法【视图类中方法任意命名】,要继承ViewSet - 以后想继承
GenericAPIView,但想改变路由写法【视图类中方法名任意命名】,要继承GenericViewSet

视图层大总结
-
两个视图基类
APIView、GenericAPIView -
五个视图扩展类,不是视图类,必须配合GenercAPIView
CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin,ListModelMixin -
9个视图子类,是视图类,只需要继承某几个就可以实现功能
ListAPIView, CreateAPIView, ListCreateAPIView,RetrieveAPIView, UpdateAPIView, DestroyAPIView, RetrieveDestroyAPIView, RetrieveUpdateAPIView, RetrieveUpdateDestroyAPIView -
视图集
-
ModelViewSet# 视图类 from rest_framework.viewsets import ModelViewSet class BookAPIView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer # 路由 path('books/', views.BookAPIView.as_view({'get': 'list', 'post': 'create'})), path('books/<int:pk>/', views.BookAPIView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})) -
ReadOnlyModelViewSet# 路由 urlpatterns = [ path('books/', views.BookView.as_view({'get': 'list'})), path('books/<int:pk>/', views.BookView.as_view({'get': 'retrieve'})), ] # 视图类 class BookView(ReadOnlyModelViewSet): # 查询所有,新增一个 queryset = Book.objects.all() serializer_class = BookSerializer -
ViewSetMixin不是视图类,是魔法方法,重写了as_view,
# 路由 path('test/',views.TestVAPIView.as_view({'get':'login'})) # 视图 class TestVAPIView(ViewSetMixin,APIView): def login(self,request): return Response('登录功能')当请求来了,就优先执行视图类中的
login方法,当请求方法多时,从左到右依次执行。 -
ViewSet :ViewSetMixin + APIView -
GenericViewSet : ViewSetMixin + GenericAPIView
eg:发送短信接口,视图类叫
SendView,方法叫send_sms,路由配置变了get --> send_sms# 视图 class SendView(ViewSet): def send_sms(self,request): phone = request.query_params.get('phone') print('发送成功%s' % phone) return Response({'code':100,'msg':'发送成功'}) # 路由 path('send/',views.SendView.as_view({'get':'send_sms'})) -

浙公网安备 33010602011771号