1.APIView基本使用
# drf:是一个第三方的app,只能在djagno上使用
# 安装了drf后,导入一个视图类APIView,所有后期要使用drf写视图类,都是继承APIView及其子类
eg:编写获取所有图书的接口
1.1使用View+JsonResponse写
路由设计:path('books/', BookView.as_view()),
class BookView(View):
def get(self, request):
book_list = Book.objects.all()
# book_list是一个queryset对象 不能直接序列化 需要通过for循环取出一个个的字典,再添加到列表里,再交给JsonResponse去序列化
res_list = []
for book in book_list:
res_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
return JsonResponse(res_list, safe=False, json_dumps_params={'ensure_ascii': False}) # JsonResponse只能序列化列表或者字典数据
1.2使用APIView+drf的Response写
class BookView(APIView): # APIView继承自django的View
def get(self, request):
book_list = Book.objects.all() # queryset不能直接序列化
res_list = []
for book in book_list:
res_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
return Response(res_list) # drf的Response序列化返回时不用设置ensure_ascii参数,会自动转换中文格式
2.APIView源码分析
# 视图类继承APIView后 执行流程就发生了变化 这个变化就是整个drf的执行流程
路由配置跟之前继承View是一样的----》找视图类的as_view---》【APIView的as_view】
第一步:点进as_view
@classmethod
def as_view(cls, **initkwargs):
# 由于我们的视图类BookView继承的是APIView 所以这里的as_view是 APIView 的as_view
view = super().as_view(**initkwargs) # 又调用了父类(View)的as_view
return csrf_exempt(view) # 这句话帮我们取消了csrf的校验
'''
补充:在函数上加装饰器
@csrf_exempt
def index(request):
pass
本质等同于 index = csrf_exempt(index)
所以我们 return csrf_exempt(view) 就等同于 return view(此时的view被csrf_exempt装饰 就取消了csrf的校验)
'''
-请求来了,路由匹成功之后会执行View类的as_view类方法内的view闭包函数(但是取消了csrf认证)
-真正的执行:执行self.dispatch---->APIView的dispatch 【这是重点】
第二步:点进dispatch #(这里的点进dispatch是APIView的点进dispatch)
def dispatch(self, request, *args, **kwargs):
# 参数里的request是django原生的request
# 下面的request 变成了drf提供的Request类产生的request对象
request = self.initialize_request(request, *args, **kwargs)
# def initialize_request(self, request, *args, **kwargs):
# ......
# return Request()
self.request = request
# 这里的self是我们视图类BookView的对象 上面这句话的作用是将新的request赋值给我们原生的request
try:
self.initial(request, *args, **kwargs) # 执行了权限、频率、权限
if request.method.lower() in self.http_method_names:
# 原生的View的dispatch的东西
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
# 在执行三大认证或者视图类的方法的过程中出现异常,那就捕获并处理异常 并正常返回
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
总结:
1.只要视图类继承了APIView就都没有csrf的认证了
2.以后视图类中使用的request对象,都已经变成drf提供的Request类的对象了
3.执行视图类的方法之前,执行了三大校验(认证、权限、频率)
4.在执行三大认证和视图类的方法中只要报错就都会被捕获异常
3.Request类源码分析
1. 视图函数中使用的request对象,已经变成了drf提供的Request类的request对象了
原生的django的request:
class BookView(View):
def get(self, request):
print(type(request)) # <class 'django.core.handlers.wsgi.WSGIRequest'>
drf的request:
class BookView(APIView):
def get(self, request):
print(type(request)) # <class 'rest_framework.request.Request'>
2. 新的request依旧能使用老的request的方法
print(request.method) # get
print(request.path) # /books/
print(request.GET) # 原来的get请求提交的参数
print(request.POST) # 原来post请求提交的参数
3.Request源码分析: rest_framework.request.Request
# 补充:__getattr__: 对象.属性时,该属性不存在会自动触发该方法的执行
class Request:
def __getattr__(self, attr): # 如果取的属性不存在就会去原生django的request中取出来。也就是说drf的request对象使用的GET、POST...全是原生django提供的。
try:
return getattr(self._request, attr)
# self._request指的就是django原生的request
# 反射 根据字符串获取属性或方法,
except AttributeError:
return self.__getattribute__(attr)
-以后想使用的所有属性或方法直接用就可以 --->drf会通过反射区原来的request中取
-新的request内部有个老的request 就是request._request
-data是个方法,被property装饰了 ,编程了数据属性
4.数据获取:
1.post请求的数据:
-原生django:urlencoded,form-data格式的数据在request.POST中取,json格式数据在request.body中取
-drf:全部去request.data中取
2.get请求的数据:
-原生的django:request.GET中取
-drf:request.query_params 或 request._request.GET 或 request.GET
# 其实request._request指的就是原生的request
3.文件数据:都是从request.FILES中取,跟之前一样
# 注意:
当编码格式是urlencoded,form-data时 request.data是 'QueryDict'
当编码格式是json时 request.data是 '普通字典'
4.序列化组件(查询所有、查询单个)
介绍:序列化组件就是drf提供的一个类,我们可以继承他,写自己的类
作用:序列化queryset或者单个对象(qs.first())
4.1:定义一个序列化类
serializer.py:
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
# 这里写要序列化的字段 如果将字段注释即为取消序列化
name = serializers.CharField()
price = serializers.IntegerField()
publish = serializers.CharField()
4.2:使用序列化类,序列化多条数据
class BookView(APIView): # APIView继承自django的view
def get(self, request): # 获取多条数据用get请求
book_list = Book.objects.all()
ser = BookSerializer(instance=book_list, many=True)
# instance表示要序列化的QuerySet对象,many=True表示要序列化多条数据 这两个参数一定要传
return Response(ser.data)
4.3:序列化单条数据
路由设计: path('books/<int:pk>', BookDetailView.as_view()),
class BookDetailView(APIView):
def get(self, request, pk): # 查询单本图需要传id
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book) # 单条数据不需要指定many=True
return Response(ser.data)
5.反序列化(新增、修改)
5.1:序列化类编写
增加和修改数据需要重写create和update方法
class BookSerializer(serializers.Serializer):
# 这里写要序列化的字段 如果将字段注释即为取消序列化
name = serializers.CharField()
price = serializers.IntegerField()
publish = serializers.CharField()
# 重写create方法
def create(self, validated_data): # validated_data 校验过后的数据
res = Book.objects.create(**validated_data)
return res
# 重写update方法
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
5.2:新增
class BookView(APIView):
def post(self, request): # 新增用post请求
# 前端传入的数据 从request.data中取出来
ser = BookSerializer(data=request.data)
if ser.is_valid(): # 如果校验通过则完成新增。只不过我们这里没有写校验规则
ser.save() # 调用save会触发BookSerializer的save方法,判断了,如果instance有值执行update,没有值执行create,但是我们目前的序列化类没有这两个方法。我已我们需要重写这两个方法,不然这里会报错。
return Response(ser.data)
else:
return Response(ser.errors)
5.3:修改
class BookSerializer(serializers.Serializer):
def put(self,request,pk): # 修改用put请求
book=Book.objects.filter(pk=pk).first()
# instance有值表示修改
ser = BookSerializer(instance=book, data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
5.4:删除
class BookDetailView(APIView):
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response()
作业
1 继承apiview写5个接口()
2 fbv写---》写个装饰器,装饰在视图函数,只要一装饰,以后的request就可以使用request.data,这个data无论是那种编码格式,都有数据
1 继承apiview写5个接口()
class BookView(APIView):
def get(self, request): # 查所有
book_list = Book.objects.all()
res_list = []
for book in book_list:
res_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
return Response(res_list)
def post(self, request): # 增
res_dict = {}
for key in request.data:
res_dict[key] = request.data.get(key)
print(res_dict)
Book.objects.create(**res_dict)
return Response(res_dict)
class BookDetailView(APIView):
def get(self, request, pk): # 查一本
res_dict = {}
book = Book.objects.filter(pk=pk).first()
res_dict['name'] = book.name
res_dict['price'] = book.price
res_dict['publish'] = book.publish
return Response(res_dict)
def put(self, request, pk): # 改
res_dict = {}
for key in request.data:
res_dict[key] = request.data.get(key)
Book.objects.filter(pk=pk).update(**res_dict)
return Response(res_dict)
def delete(self, request, pk): # 删
Book.objects.filter(pk=pk).delete()
return Response()
2 fbv写---》写个装饰器,装饰在视图函数,只要一装饰,以后的request就可以使用request.data,这个data无论是那种编码格式,都有数据
def outer(func_name):
def inner(request, *args, **kwargs):
if request.POST:
request.data = request.POST
if request.body:
request.data = json.loads(request.body)
res = func_name(request, *args, **kwargs)
return res
return inner
@outer
def book_view(request):
print(request.data)
return HttpResponse()