url.py
path('books/', BookView.as_view())
view.py
class BookView(APIView):
pass
1.我们看自己是否有as_view()方法,没有,那我们就来到继承的APIView类中
view = super().as_view(**initkwargs) # 这是调用了父类View的as_View方法
return csrf_exempt(view) # 只要是继承了APIView类的视图函数都失去csrf的保护
在View中的dispatch方法的时候,我们需要先看APIView类中有没有该方法,来到APIView中的dispatch方法:
request = self.initialize_request(request, *args, **kwargs) # 创建了一个新的request,后面使用的不再是Django中的request方法了
self.request = request # 更换掉request方法
try:
self.initial(request, *args, **kwargs)
# Get the appropriate handler method(获取到适当的处理程序方法)
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), # 这里判断是否存在于7大方法中,eg:get
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs) # 返回request=get(request,*args,**kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
APIView类
1.包装新的request
2.执行了认证,权限,频率
3.处理了全局异常
4.包装了request对象
drf是一个第三方的app,只能在django中使用
安装好drf之后,需要导入APIView视图类,后期使用drf写视图类,都是继承了APIView视图类或其子类
当我们使用request.data的时候,三种数据格式的结果方式是
1.urldecoded和form_data的结果是QueryDict形式
2.Json的结果是字典
使用View和JsonResponse写
继承的是View
当我们使用APIView+drf的Response写的时候,我们不要忘记注册rest_framework这个app
继承APIView视图类,可以使用request.data
原生的request.POST中,只有urlencoded和form-data格式提交的数据可以获取到,json格式提交的数据在body中,需要我们自己处理;但是在drf中,request中有一个data,data中可以取到任意的编码提交的数据
request._request.GET -最初始的request。 # 相当于是request.GET
request._request -新request
那么为什么新的request可以与老request执行结果相同。
因为触发了__getattr__魔法方法,何为魔法方法?不需要主动调用,某种状态下会触发。
__getattr__ 对象.属性 会触发并执行
def __getattr__(self, attr):
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
Request类源码分析:
视图类中使用的request对象,已经变成了drf中提供的request类对象了
1.原生的django的request是这个类的对象:django.core.handlers.wsgi.WSGIRequest
2.drf的request是这个类的对象:rest_framework.request.Request
虽然request已经不是以前的request了,但是用起来还是跟以前一样,只是加了几个特殊属性
eg:
print(request.method) # get
print(request.post) # 路由/books/
print(request.GET) # 原来的get请求提交的参数
print(request.POST) # 原来的post请求提交的参数
在源码中data其实是一个方法,只是被property装饰了,将方法变为了属性
我们要获取get方法携带回来的数据的时候,我们使用query_params 等同于request._request.GET 或 request.GET
文件数据还是从request.FILES中获取
view.py
from .serializers import BookSerializers
# 获取全部的数据
class BookView(APIView):
def get(self, request):
book_list = Book.objects.all()
ser_result = BookSerializers(instance=book_list, many=True) # 根据需求序列化数据
return Response(ser_result.data) # 返回响应出序列化后的数据
# 新增数据
def post(self, request):
data = request.data
ser_result = BookSerializers(data=data)
if ser_result.is_valid(): # 校验数据
ser_result.save() # 保存数据,这里我们需要重写create方法
return Response(ser_result.data)
else:
return Response(ser_result.errors)
# 获取到单个数据
class BookDetailView(APIView):
def get(self, request, pk):
book = Book.objects.all().filter(pk=pk).first()
ser_result = BookSerializers(instance=book) # 单个数据many不用写
return Response(ser_result.data)
def put(self, request, pk):
book = Book.objects.all().filter(pk=pk).first()
ser_result = BookSerializers(instance=book, data=request.data) # request.data修改到book
if ser_result.is_valid():
ser_result.save() # 保存数据,但是我们也还要重写update方法
return Response(ser_result.data)
else:
return Response(ser_result.errors)
def delete(self, request, pk):
Book.objects.all().filter(pk=pk).delete()
return Response()
这里我们需要设置一个py文件,根据自己的需求设置序列化数据,还有数据的增加和修改需要重写create和update两个方法
serializers.py
from rest_framework import serializers
from .models import Book
# 要序列化的数据
class BookSerializers(serializers.Serializer):
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
def create(self, validated_data): # validated_data是过滤之后的数据
data = Book.objects.create(**validated_data)
return data
def update(self, instance, validated_data): # instance是数据实列,validated_data是一个字典
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.publish = validated_data.get('publish')
instance.save()
return instance
drf中request有一个data方法,但是在源码中data其实是一个方法,只是被property装饰了,将方法变为了属性
1.不携带任何数据的时候,request.data返回的是空字典
2.urlencoded数据,request.POST与request.data都能接收到数据
3.Json格式数据,只能request.data接收,数据是先放在body中,然后通过loads反序列化,然后放在data中
request.data 包含了所有解析的内容,包括文件与非文件的输入
使用drf的时候,继承APIView视图类之后,drf提供了很多的view,它们都是来自于APIView类中
会包装出一个新的request,在视图中使用的时候,跟原来的情况差不多
但是需要注意两点:
1.取post数据的时候,不要使用request.POST取,而是使用request.data取值
2.取get提交的数据的时候,不要从request.GET中取,而是要从query_params中取
如果我们强烈的想使用原生django中的request,那我们应使用request._request
当执行form-data的时候,想打印request.data的时候,会报错,因为该格式可以携带文件数据,可能文件数据过大,防止数据过大破坏,所以就不会执行
当视图类继承的是View视图类的时候,使用put方法的时候,用request.POST取不到值
.content_type:
request.content_type 返回HTTP请求正文的媒体类型的字符串对象,如果未提供媒体类型,则返回空字符串。也就是正文的格式。
在drf中是可以用来访问请求的内容类型
APIView源码分析
路由层
path('books/', views.BookView.as_view())
视图层
class BookView(APIView):
def get(self, request):
book_list = Book.objects.all()
serial = BookSerializer(instance=book_list, many=True)
return Response(serial.data)
1.在路由层中BookView.as_view()先从BookView中查找as_view方法 找不到
2.再从父类APIView中查找as_view方法 在APIView中发现有as_view方法 加括号执行
3.在as_view中关键一步调用了父类原生View中的as_view方法view = super().as_view(**initkwargs)
最后将view套上装饰器csrf_exempt(view)后的函数地址返回到路由后面path('books/', csrf_exempt(view)),
这步操作可以免去自定义类中视图方法post,put...的csrf验证
最后当路由匹配成功后会加括号执行csrf_exempt(view)(request, *args, **kwargs),进而执行下面的步骤
4.而在view方法中会调用dispatch方法 这时依然是按照从对象到类,再到父类 再到父类的父类的查找顺序
这时会优先查找到并执行父类APIView中的dispatch方法
在这个dispatch方法中干了三件事
(1.认证、频率、权限 2.进一步扩写request对象的方法属性 3.使用反射方法执行自定义视图类中对应的方法)
1.调用APIView对象的方法request = self.initialize_request(request, *args, **kwargs)
将原生django的request对象转化为DRF的request对象 让request对象保留原有的所有方法
同时又新增了request.data属性(@property 本质上是一个方法) 这个方法将body中提交的数据反序列化之后都封装到了request.data中,
也就是说无论是urlencoded格式、json格式还是form-data格式的数据,都封装进了request.data方法(属性)中
但同时request.POST和request.FILES依然可以获取到对应的数据 需要注意的是这时保留在body中的
json格式数据就不能通过request.body获取了 封装过后的request已禁止了此方法
此外get请求后面?携带的数据依然通过query_params或request.GET获取
2.在dispatch方法中用异常捕获执行了self.initial(request, *args, **kwargs)认证、频率、权限这步操作
若在执行认证 频率 权限操作时报错了 就会返回其他数据 不会正常执行自定义视图类BookView中的get、post
delete...等方法
3.在dispatch中用异常捕获执行了反射方法 从自定义类中获取request.method对应的方法 若有就加括号执行此方法,并返回相应的结果
若获取不到,则执行另外的代码 最终报错没有定义此方法
path('books/', BookView.as_view())
class BookView(APIView):
def get(self, request):
book_list = Book.objects.all()
ser = BookSerializer(instance=book_list, many=True)
return Response(ser.data)
源码:
class APIView(View):
@classmethod
def as_view(cls, **initkwargs)
view = super().as_view(**initkwargs)
view.cls = cls
return csrf_exempt(view)
def dispatch(request, *args, **kwargs):
reqeust = self.initialize_request # 将request转化为drf的request
self.reqeust = reqeust # 替换原生request
# 利用异常捕获 若执行认证、频率、权限、获取对象的方法报错 则会返回其他异常信息
try:
self.initial(request, *args, **kwargs) # 频率 认证 权限
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), # 利用反射获取自定义对象中的get、post...方法
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs) # 将反射获取到的方法执行结果赋值给response 最后返回response
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
class View:
def as_vew(cls, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs) # 这里的cls就是自定义类BookView,是这个类直接来调用的as_view方法(绑定给类的方法)
return self.dispatch(reqeust, *args, **kwargs)
return view
当路由匹配成功后加括号执行csrf_exempt(view)(request, *args, **kwargs)
最后执行APIView的dispatch方法