drf视图组件与视图基类
简介: 两个视图基类:PIView,GenericAPIView,五个扩展类:ListModelMixin,CreateModelMixin ,RetieveModelMixin,UpdateModelMixin,DestoryModelMixin,九个视图子类与视图集
drf视图组件
视图就是视图类,我们之前学过的APIView就是drf提供的基类。
APIView与原生View区别:
1.传入到视图类中的是drf的Request对象而不是django的request对象
2.视图类应该返回 drf的Request对象
3.任何异常都会被捕获到,并处理
4.在进行dispatch()前 会进行3大认证即认证,权限,频率
两个视图基类
APIView
再写一遍,练习加倍
APIView+ModelSerializer+Resposne写5个接口
表
from django.db import models
# Create your models here.
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
def publish_detail(self):
return {'name': self.publish.name, 'addr': self.publish.addr}
def author_list(self):
l = []
for author in self.authors.all():
l.append({'name': author.name, 'phone': author.phone, 'age': author.detail.age, 'addr': author.detail.addr})
return l
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
phone = models.CharField(max_length=11)
detail = models.OneToOneField(to='Detail', on_delete=models.CASCADE)
class Detail(models.Model):
age = models.CharField(max_length=4)
addr = models.CharField(max_length=32)
视图类
from .models import Book
from .serializer import BookSerializer
class BookView(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()
return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
"""这里的ser.data为什么能拿到数据是因为他把新增的对象序列化成了一个字典,前提是必须在create方法内返回新增对象"""
else:
return Response({'code': 101, 'msg': ser.errors})
class BookDetailView(APIView):
def get(self, request, pk):
books = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=books)
return Response(ser.data)
def put(self, request, pk):
books = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=books, data=request.data)
if ser.is_valid():
ser.save()
return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
else:
return Response({'code': 101, '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
fields = ['name', 'price', 'publish_detail', '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},
}
路由
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookView.as_view()),
path('books/<int:pk>/', views.BookDetailView.as_view()),
]
APIView类属性简介
renderer_classes # 响应格式
parser_classes #能够解析的请求格式
authentication_classes #认证类
throttle_classes #频率类
permission_classes #权限类
GenericAPIView
重要属性
"""
queryset: 要序列化或反序列化 表模型的数据(对象)
serializer_class: 使用的序列化类
lookup_field 查询书籍路由分组匹配的名<int:pk>
如果路由中不是pk可以改直接在视图类中 lookup_field = pk
filter_backends 过滤类配置
pagination_class 分页类配置
"""
重要方法:
"""
1.get_queryset 获取序列化的对象 queryset对象[{对象1},{对象2}]
2.get_object 获取单个对象 不需要传pk
3.get_serializer 获取=后面的序列化类
get_serializer_class 重写指定序列化类
4.filter_queryset 过滤相关
"""
别的代码不变只是views中需要继承GenericAPIView
from rest_framework.generics import GenericAPIView
class BookView(GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self, request):
"""
get_queryset 固定的方法拿到对象
get_serializer 固定的方法 后期可以重写get_serializer_class指定序列化类
"""
objs = self.get_queryset()
ser = self.get_serializer(instance=objs,many=True)
return Response(ser.data,status=201,headers={'a':'b'})
def post(self, request):
ser = self.get_serializer(data=request.data)
if ser.is_valid():
ser.save()
return Response({'code': 100, 'msg': '新增成功'})
else:
return Response({'code': 101, 'msg': ser.errors})
class BookDetailView(GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
"""
get_object 获取单个对象
"""
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()
return Response({'code': 100, 'msg': '新增成功'})
else:
return Response({'code': 101, 'msg': ser.errors})
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({'code': 100, 'msg': '删除成功'})
五个扩展类
CreateModelMixin 新增 post
ListModelMixin 查询所有 get
RetrieveModelMixin 查询单个
UpdateModelMixin 修改 put
DestroyModelMixin 删除
"统一在from rest_framework.mixins import 内"
基于五个视图扩展类编写
views
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin
class BookView(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): # 参数也可以使用*args,**kwargs来传
return self.retrieve(request,pk)
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 继承了 ListModelMixin,GenericAPIView get方法
CreateAPIView 继承了CreateModelMixin,GenericAPIView post方法
ListCreateAPIView 视图子类呢继承了ListModelMixin,CreateModelMixin,GenericAPIView 也就是说拥有了上面两个子类的功能
"""
查询所有增加一个
class BookView(ListCreateAPIView):
"ListCreateAPIView内部继承了ListModelMixin,CreateModelMixin已经拥有get与post方法了"
queryset = Book.objects.all()
serializer_class = BookSerializer
"""
二,查询一个,修改一个,删除一个
RetrieveView 继承了 RetrieveModelMixin,GenericAPIView ,get方法
UpdateView 继承了 UpdateModelMixin,GenericAPIView ,put方法
DestroyView 继承了 DestroyModelMixin,GenericAPIView ,delete方法
RetrieveUpdateDestroyAPIView 继承了上述三个继承的父类RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericAPIView
拥有了他们含有的所有方法
"""
# 查询一个,修改一个,删除一个
class BookView(RetrieveUpdateDestroyAPIView):
"父类继承了RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView 拥有了get,put ,delete方法"
queryset = Book.objects.all()
serializer_class = BookSerializer
总结
ListAPIView ---> ListModelMixin,GenericAPIView get方法,查询所有
CreateAPIView ---> CreateModelMixin,GenericAPIView post方法,增加一个
ListCreateAPIView ---> ListModelMixin,CreateModelMixin,GenericAPIView get方法,post方法 查询所有增加一个
RetrieveAPIView ---> RetrieveModelMixin,GenericAPIView get 查询一个
DestroyAPIView ---> DestroyModelMixin,GenericAPIView delete 删除一个
UpdateAPIView ---> UpdateModelMixin,GenericAPIView put 修改一个
RetrieveDestroyAPIVIew ---> RetrieveModelMixin,DestroyModelMixin,GenericAPIView
RetrieveUpdateAPIView ---> RetrieveModelMixin,UpdateModelMixin,GenericAPIView
RetrieveUpdateDestroyAPIView ---> RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView
我们发现使用这种方法还是要写两个视图,不然会有2个get方法,路由的路径也不一样,drf 给我们提供了一个类。
ModelViewSet 类
这个类继承了5个扩展类。那么它就拥有了5个接口,但是一旦继承了这个类路由写法就变了。
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
基于ModelViewSet 类编写接口
# 查询所有,新增一个
from rest_framework.viewsets import ModelViewSet
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
路由层
# 根据请求方法,映射对应的方法
urlpatterns = [
path('admin/', admin.site.urls),
path('books/',views.BookView.as_view({'get':'list','post':'create'})),
path('books/<int:pk>/',views.BookView.as_view({'get':'retrieve','put':'update','delete':'destroy'}))
ReadOnlyModelViewSet 类 只读
如果继承它那么就只有查询一个和查询所有接口
class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
GenericViewSet):
他继承了查询一个接口和查询所有接口
基于ReadOnlyModelViewSet编写 2个只读接口
from rest_framework.viewsets import ReadOnlyModelViewSet
class BookView(ReadOnlyModelViewSet): # 只有查询单个和查询所有
queryset = Book.objects.all()
serializer_class = BookSerializer
"需要注意的是他的路由只能填写2个接口"
路由层:
urlpatterns = [
path('admin/', admin.site.urls),
path('books/',views.BookView.as_view({'get':'list'})),
path('books/<int:pk>/',views.BookView.as_view({'get':'retrieve'}))
使用两种这种方法我们只需要编写一个视图类,但是路由的写法变了,为什么变?需要研究源码
路由写法源码研究
我们在继承ModelViewSet与ReadOnlyModelViewSet的时候发现它们两还继承了一个GenericViewSet类,进入其源码发现他继承了
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
ViewSetMixin # 没见过这个
GenericAPIView # 两个视图基类其中一个
之前在继承GenericAPIView编写接口的时候路由并没有变化,所以问题出在我们没见过的ViewSetMixin上。研究它的源码
请求来了(get,post),路由匹配成功,匹配成功books,会执行views.BookView.as_view({'get': 'list', 'post': 'create'})()
读as_view此时这个我们的视图类中内并没有as_view去父类中查找 在GenericViewSet的父类ViewSetMixin中找到了as_view方法
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
# 如果没有传actions(路由中的{'get':'list'}),直接抛异常,路由写法变了后,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'})() 返回了view并去除了csrf校验,执行view(),# 此时执行ViewSetMixin了as_view内的view()
def view(request, *args, **kwargs):
#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)
return self.dispatch(request, *args, **kwargs) #从视图类往上寻找,dispatch是APIView的
其本质就是:
-
只要继承了ViewSetMixin的视图类,路由的写法就变了,根本原因是重写了as_view
-
路由编写的方法变成了as_view({‘get’:‘list’})
只要传入actions参数,以后get就会被as_view内的view方法利用反射编程list,访问get就是访问list的(映射)
-
其他执行顺序和以前一样,以后在视图类编写方法名只需要在路由层中做好映射。
扩展
我们在继承ModelViewSet时候发现这个rest_framework.viewsets包下还有几个类。
'''
ModelViewSet 5个试图扩展类+ViewSetMixin+GenericAPIView
ReadOnlyModelViewSet 2个试图扩展类+ViewSetMixin+GenericAPIView 只读的两个
ViewSetMixin 重写了as_view,只要继承他,以后路由写法变成了映射方法 需要配合9个视图类与GenericAPIView
ViewSet ViewSetMixin+ APIView
GenericViewSet ViewSetMixin+ GenericAPIView
'''
继承APIView来写接口,变路由写法({'请求方法':'任意方法名'})就要继承ViewSet
继承GenericAPIView来写接口,变路由写法({'请求方法':'任意方法名'})就要继承 GenericViewSet
总结
1. 两个试图基类
-APIView,GenericAPIView
2. 5个试图扩展类,不是视图类,必须配合GenericAPIView
-ListModelMixin # get 查询所有
-CreateModelMixin # post 新增一个
-RetieveModelMixin # get 查询一个
-UpdateModelMixin # put 修改一个
-DestoryModelMixin # delete 删除一个
3. 9 个视图子类,是视图类,只需要继承其中某一个即可就可以使用那一个的功能
ListAPIView ---> ListModelMixin,GenericAPIView get方法,查询所有
CreateAPIView ---> CreateModelMixin,GenericAPIView post方法,增加一个
ListCreateAPIView ---> ListModelMixin,CreateModelMixin,GenericAPIView get方法,post方法 查询所有增加一个
RetrieveAPIView ---> RetrieveModelMixin,GenericAPIView get 查询一个
DestroyAPIView ---> DestroyModelMixin,GenericAPIView delete 删除一个
UpdateAPIView ---> UpdateModelMixin,GenericAPIView put 修改一个
RetrieveDestroyAPIVIew ---> RetrieveModelMixin,DestroyModelMixin,GenericAPIView
RetrieveUpdateAPIView ---> RetrieveModelMixin,UpdateModelMixin,GenericAPIView
RetrieveUpdateDestroyAPIView ---> RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView
4. 视图集
ModelViewSet 5个试图扩展类+ViewSetMixin+GenericAPIView
"只需要写两行五个接口都有"
ReadOnlyModelViewSet 2个试图扩展类+ViewSetMixin+GenericAPIView 只读的两个
"只需要写两行两个只读接口有了"
ViewSetMixin 重写了as_view,只要继承他,以后路由写法变成了映射方法 需要配合9个视图类与GenericAPIView
ViewSet ViewSetMixin+ APIView
GenericViewSet ViewSetMixin+ GenericAPIView