一:APIView和View的区别
a:rest-framework对原Request进行了封装
--request.query_params 存放的是我们get请求的参数
--request.data 存放的是我们所有的数据,包括post请求的以及put,patch请求
二:对Views视图进行封装
a:观察几个请求对应的方法的共同之处
1:models.Book.objects.all()或者models.Book.objects.filter(**kwargs)
2:book_serializers = BookSerializers(book_obj,data=request.data,partial=True) #参数不一样
def put(self,request,id):
print(request.data)
book_obj = Book.objects.filter(id=id).first()
# partial True 表示可以进行部分字段跟新,Flase 表示全部跟新,默认False
book_serializers = BookSerializers(book_obj,data=request.data,partial=True)
#需要重写 updata
if book_serializers.is_valid():
book_serializers.save()
return Response(book_serializers.validated_data)
else:
return Response(book_serializers.errors)
def delete(self,request,id):
book_obj = Book.objects.filter(id=id).first()
book_obj.delete()
return Response("")
3:进行初次封装
注意点:
i:定义序列化和反序列化类 并进行设置 serializer_class = BookSerializers
II:获取到对应的对象(可以时多个也可以是单个)query_set = models.Book.objects.all()
III:创建一个类 GenericApIview 将上面两个属性进行通过方法获取
IV:Mixin类对不同的方法进行封装。并因为里面没有 query_set和serializer_class属性。所以需要和GenericApiView类进行混合继承。
from SerDemo import models
from rest_framework.views import APIView #视图函数继承APIView 不是View
from rest_framework.response import Response #用于返回值
#BookSerializers 自己定义的序列化规则
from SerDemo.serializers import *
class GenericAPIView(APIView):
query_set = None
serializer_class = None
def get_query_set(self):
return self.query_set
def get_serializer_class(self,*args,**kwargs):
return self.serializer_class(*args,**kwargs)
class ListModelMixin(object):
def list(self, request, *args, **kwargs):
book_obj = self.get_query_set()
# book_obj = models.Book.objects.all()
# 序列化多组数据 和一个对象,需要many=TRUE 默认一个
ret = self.get_serializer_class(book_obj, many=True)
# ret =BookSerializers(book_obj,many=True)
# 序列化的数据在data属性中
return Response(ret.data)
class CreateModelMixin(object):
def create(self,request,*args,**kwargs):
data = self.get_serializer_class(data=request.data)
# 进行数据验证,并且在BookSerializers中重写create方法
if data.is_valid():
data.save()
return Response(data.data)
else:
return Response(data.errors)
class RetrieveModelMixin(object):
def retrieve(self,id,request,*args,**kwargs):
book_obj = self.get_query_set().filter(id=id).first()
book = self.get_serializer_class(book_obj)
return Response(book.data)
class UpdateModelMixin(object):
def update(self, request, id, *args, **kwargs):
book_obj = self.get_query_set().filter(pk=id).first()
book_ser = self.get_serializer(book_obj, data=request.data, partial=True)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.validated_data)
else:
return Response(book_ser.errors)
class DestroyModelMixin(object):
def destroy(self, request, id, *args, **kwargs):
queryset = self.get_query_set()
try:
queryset.get(pk=id).delete()
return Response("")
except Exception as e:
return Response("信息有误")
class BookApiView(GenericAPIView,ListModelMixin,CreateModelMixin):
query_set=models.Book.objects.all()
serializer_class=BookSerializers
def get(self,request,*args,**kwargs):
# book_obj =self.get_query_set()
# book_obj = models.Book.objects.all()
#序列化多组数据 和一个对象,需要many=TRUE 默认一个
# ret = self.get_serializer_class(book_obj,many=True)
# ret =BookSerializers(book_obj,many=True)
#序列化的数据在data属性中
return self.list(request, *args, **kwargs)
def post(self,request,*args,**kwargs):
return self.create(request,*args,**kwargs)
class BookEditView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
query_set = models.Book.objects.all()
serializer_class = BookSerializers
def get(self, request,id,*args,**kwargs):
return self.retrive(id,request,*args,**kwargs)
def put(self,request,id,*args,**kwargs):
return self.update(request,id,*args,**kwargs)
def delete(self,request,id,*args,**kwargs):
return self.destroy(request, id, *args, **kwargs)
4:再次优化封装
现在有两个问题
一:view中有两个方法。能不能合成一个
二:对应的View视图函数中只有对应的方法调用,不存在任何逻辑。能否通过url映射取出对应的方法
a:修改url
--观察上面修改的方法,可以看出link/ 路径下 get ---调用类了 self.list post调用了 self.create方法
--可以得出结论 如果我们按下面袖该的url分发并执行对应的函数,那么我们就可以进一步简化
--但是原有的方法as_view方法不能进行传参数
urlpatterns = [
path('link/', views.BookModelSetView.as_view({"get":"list","post":"create"})),
path('retrieve/',
views.BookModelSetView.as_view({"get":"retrieve","put":"update","delete":"destroy"}))
]
b:解决as_view()方法传参
i:导入ViewSetMixin类
from rest_framework.viewsets import ViewSetMixin
II:阅读对应的源码:
--as_view()进行重写,然后看view方法(不建议改动源码,我是为了学习注释。)
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
"""
Because of the way class based views create a closure around the
instantiated view, we need to totally reimplement `.as_view`,
and slightly modify the view function that is created and returned.
"""
# The name and description initkwargs may be explicitly overridden for
# certain route confiugurations. eg, names of extra actions.
cls.name = None
cls.description = None
# The suffix initkwarg is reserved for displaying the viewset type.
# This initkwarg should have no effect if the name is provided.
# eg. 'List' or 'Instance'.
cls.suffix = None
# The detail initkwarg is reserved for introspecting the viewset type.
cls.detail = None
# Setting a basename allows a view to reverse its action urls. This
# value is provided by the router through the initkwargs.
cls.basename = None
# actions must not be empty
if not actions:
raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`")
# sanitize keyword arguments
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r" % (
cls.__name__, key))
# name and suffix are mutually exclusive
if 'name' in initkwargs and 'suffix' in initkwargs:
raise TypeError("%s() received both `name` and `suffix`, which are "
"mutually exclusive arguments." % (cls.__name__))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
# We also store the mapping of request methods to actions,
# so that we can later set the action attribute.
# eg. `self.action = 'list'` on an incoming GET request.
self.action_map = actions
# Bind methods to actions
# This is the bit that's different to a standard view
'''
1:actions={"get":"list","post":"create"} as_view()方法中传递的参数
2:循环后结果为
methon ="get" #key
action ='list' #对应的方法字符串
'''
for method, action in actions.items():
'''
3:通过getattr将字符串反射对应的方法名
handler = list #循环将每一个字符传,修改对应的放啊
4:通过setattr(self, method, handler) 得到的结果为
set.method =self.handler -->self.get=self.list.
5:成功映射对应的方法
'''
handler = getattr(self, action)
setattr(self, method, handler)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# And continue as usual
'''
6:重新进行路由分发,并且已经修改成功方法
'''
return self.dispatch(request, *args, **kwargs)
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
# We need to set these on the view function, so that breadcrumb
# generation can pick out these bits of information from a
# resolved URL.
view.cls = cls
view.initkwargs = initkwargs
view.actions = actions
return csrf_exempt(view)
c:最后进行view编写:
from rest_framework.viewsets import ViewSetMixin
# 定义一个类,用来简化BookModelSetView的继承类的长度,不是很重要
class ModelViewSet(ViewSetMixin,GenericAPIView,RetrieveModelMixin,
UpdateModelMixin,DestroyModelMixin,CreateModelMixin,ListModelMixin):
pass
class BookModelSetView(ModelViewSet):
query_set = models.Book.objects.all()
serializer_class = BookSerializers
5:使用rest_framework自带的方法实现以上功能
--导入 from rest_framework.viewsets import ModelViewSet
--参数是queryset和serializer_class
--路由修改id 成pk
--该方法和上面自修改方法本质上相同。
--使用自带的方法简便,但是有时候并不需要那么多的方法,所以自定义方法可以由于简化请求方法。
from rest_framework.viewsets import ModelViewSet
class BookModelSetView(ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = BookSerializers
urlpatterns = [
path('link/', views.BookModelSetView.as_view({"get":"list","post":"create"})),
path('retrieve/',
views.BookModelSetView.as_view({"get":"retrive","put":"update","delect":"destroy"}))
]
三:小结
1:以上用到的方法均在以下4个类中,可以查看下源码
from rest_framework import views,generics,mixins,viewsets
四:路由系统
了解下即可,最好不要用这种方法
from .views import BookView
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r"book", BookView)
urlpatterns = [
]
urlpatterns += router.urls