APIView类
【一】APIView类的介绍
【1】介绍
Django REST Framework(DRF)中的 APIView
类是一个非常重要的基类,用于定义基于类的视图(Class-based Views)来处理 RESTful API 请求。它提供了强大的功能和灵活的扩展点,使得编写 API 视图变得更加简单和灵活。
【2】主要特点和用法
- 处理 HTTP 请求方法:
APIView
类允许你在一个类中处理多个 HTTP 请求方法(GET、POST、PUT、DELETE 等)。你只需要在APIView
类中定义对应的方法,例如get()
、post()
、put()
、delete()
等。 - 序列化和反序列化数据: DRF 中的
APIView
类与序列化器(Serializer)类紧密集成,它可以方便地处理请求数据的反序列化以及响应数据的序列化。你可以在APIView
中使用序列化器来验证和处理请求数据,以及将模型实例转换为 JSON 格式的响应数据。 - 权限和认证:
APIView
类提供了内置的权限和认证支持,可以通过设置permission_classes
和authentication_classes
属性来配置权限和认证。这使得你可以轻松地实现对 API 视图的权限控制和用户身份验证。 - 分页和过滤: DRF 中的
APIView
类还支持分页和过滤,可以通过设置pagination_class
和filter_backends
属性来启用分页和过滤功能。这使得你可以处理大量数据并向客户端提供分页结果。 - 异常处理:
APIView
类提供了异常处理功能,可以通过重写handle_exception()
方法来自定义异常处理逻辑。这使得你可以捕获和处理 API 视图中发生的异常,并返回合适的 HTTP 响应。 - URL 路由:
APIView
类不直接处理 URL 路由,而是通过 Django 的 URL 路由系统将 URL 映射到对应的 API 视图类。你需要在 URL 配置中将 URL 映射到具体的APIView
类。
【3】做了哪些事?
- 去除了csrf认证
- 包装了新的request
- 做了三大认证
- response做了统一返回,处理全局异常
【二】封装的data
通常,当客户端发不同数据格式的post请求时,对于django而言,不能全部都用request.POST方法拿到数据。当客户端以urlencoded或者form-data的格式发来数据可以通过request.POST方法接收,其数据格式是一个类似于字典的对象。但是对于application/json的数据格式则无法接收到,需要自己从request.body里面手动转换,很麻烦
但是对于APIView类封装好的request对象来说,它对比原来的request有一个data属性,可以直接拿到处理好的数据,无论是以什么数据格式发送。
【1】基于View的五个接口
class BookView(View):
# 查询所有
def get(self, request):
obj_list = models.Book.objects.all()
if not obj_list:
return_data = {
'code': 300,
'message': '暂无数据,查询失败'
}
return JsonResponse(return_data)
data_list = []
# 手动遍历 处理数据格式 很麻烦!!!
for obj in obj_list:
title = obj.title
price = obj.price
publish = obj.publish
data = {
'title': title,
'price': price,
'publish': publish
}
data_list.append(data)
return_data = {
'code': 200,
'message': '查询所有书籍成功',
'results': data_list
}
return JsonResponse(return_data)
# 添加书籍
def post(self, request):
book_info = json.loads(request.body)
models.Book.objects.create(**book_info)
return_data = {
'code': 200,
'message': '创建图书成功'
}
return JsonResponse(return_data)
class BookDetailView(View):
# 查询一本
def get(self, request, pk):
obj = models.Book.objects.filter(pk=pk).first()
if not obj:
return_data = {
'code': 300,
'message': '查无此书'
}
return JsonResponse(return_data)
return_data = {
'code': 200,
'message': '查询图书成功',
'result': {
'title': obj.title,
'price': obj.price,
'publish': obj.publish,
}
}
return JsonResponse(return_data)
def delete(self, request, pk):
# 删除书籍
res = models.Book.objects.filter(pk=pk).delete()
if not res[0]:
return_data = {
'code': 300,
'message': '没有找到要删除的图书'
}
return JsonResponse(return_data)
return_data = {
'code': 200,
'message': '删除图书成功'
}
return JsonResponse(return_data)
def put(self, request, pk):
# 修改书籍
book_info = json.loads(request.body)
res = models.Book.objects.filter(pk=pk).update(**book_info)
print(res)
if not res:
return_data = {
'code': 300,
'message': '没有找到要修改的图书'
}
return JsonResponse(return_data)
return_data = {
'code': 200,
'message': '修改图书成功'
}
return JsonResponse(return_data)
【2】基于APIView写五个接口
首先要导入APIView
这个类,drf框架都很规范,从导模块就可以看出来
from rest_framework.views import APIView
以添加一本书为例子,就不需要自己转通过json转格式了,可以直接从request.data取出我传入的数据,十分方便快捷
但是要注意的是,除了以json数据格式发送的数据,通过request.data拿到的数据是真正意义上的字典,而对于其他数据格式来说,request.data拿到的是一个quearyset对象,他的value值是一个字典,在处理数据的时候要格外注意
还有一点是,APIView给我们提供了一个类,Response,以后返回数据的时候都推荐使用这个。
from rest_framework.response import Response
class BookAPIView(APIView):
info = request.data
res = models.Book.objects.create(**info)
return_data = {
'code': 200,
'message': '创建图书成功'
}
return Response(return_data)
【三】序列化类
当后端拿到前端传来的数据时,需要将其处理,最后将处理好的数据序列化再返还回去,对于序列化这一个步骤,之前都是通过手动处理,将其手动封装成字典,然后再将这个字典序列化成json格式发给前端,虽然很简单,但是十分的麻烦。
drf框架提供了一个序列化类Serializer
,它实例化得到的对象可以帮我们快速完成对数据的序列化,
在一个项目中,通常我们时新建一个py文件,在里面导入from rest_framework import serializers
,然后再写一个类继承Serializer
类,然后在类里面写想要序列化的数据对象的字段,它给我的感觉时非常像django的form组件的写法
## serializer.py
from rest_framework import serializers
class BookSerialize(serializers.Serializer):
title = serializers.CharField()
price = serializers.IntegerField()
publish = serializers.CharField()
然后就是对视图类的编写了,首先在view.py里面导入BookSerialize类,拿到类的第一反应就应该是实例化得到对象,然后需要传入对应的参数,最后就可以通过这个对象的data,直接拿到序列化好的数据。
# 这里以查询所有数据为例
class BookAPIView(APIView):
def get(self, request):
obj_list = models.Book.objects.all()
if not obj_list:
return_data = {
'code': 300,
'message': '暂无数据,查询失败'
}
return JsonResponse(return_data)
# 实例化类得到对象
# 传入参数
# instance:指的是需要序列化的对象
# many:如果需要序列化的对象为多个 就需要指的为True
ser = BookSerialize(instance=obj_list, many=True)
return_data = {
'code': 200,
'message': '查询所有书籍成功',
'results': ser.data # data方法拿到序列化好的数据
}
return Response(return_data)
【四】序列化类的校验
通常我们都是需要对前端传来的数据进行检验的,比如字段不能过长,也不能过短,字段里面不能有敏感词汇,以及两个字段的不能相同等等
drf的这个serialize组件也是给我们提供了一套规则的,他有三重验证,字段本身的验证,局部钩子,全局钩子。他的校验顺序是自身校验--->局部钩子--->全局钩子,只有上一层校验通过了才会走到下一层校验。
首先是字段本身的校验,这里举个简单的例子
# 十分的见名知意
class BookSerialize(serializers.Serializer):
title = serializers.CharField(max_length=8, min_length=3)
price = serializers.IntegerField(max_value=200, min_value=30)
publish = serializers.CharField(max_length=8, min_length=3)
局部钩子,通过定义validate_字段名(self,字段名)
方法,当校验不通过的时候需要抛出一个错误消息,这个错误消息drf也提供了,通过from rest_framework.exceptions import ValidationError
导入,最后将这个字段return出去就行了
class BookSerialize(serializers.Serializer):
title = serializers.CharField(max_length=8, min_length=3)
price = serializers.IntegerField(max_value=200, min_value=30)
publish = serializers.CharField(max_length=8, min_length=3)
# 这里规定书名不能包含'sb'
def validate_title(self, title):
if 'sb' in title:
# 抛出异常
raise ValidationError('不可以带脏话')
# 返回字段
return title
全局钩子,通过定义validate(self, attrs)
方法使用,通常需要用到多个字段做校验的时候会用到全局钩子,用法基本和局部钩子一样,返回的时候需要return attrs
class BookSerialize(serializers.Serializer):
title = serializers.CharField(max_length=8, min_length=3)
price = serializers.IntegerField(max_value=200, min_value=30)
publish = serializers.CharField(max_length=8, min_length=3)
def validate(self, attrs):
if attrs.get('title') == attrs.get('publish'):
raise ValidationError('书名不能与社名相同')
return attrs
【五】渲染错误消息
上面说到的抛出的异常,如ValidationError('书名不能与社名相同')
是可以渲染到相应消息里的,比如我要进行添加一本图书的操作,可是经过校验,我给出的数据并不通过,这时候返回给前端的数据的message就可以通过序列化类实例的对象的errors属性拿到。
这里以添加一本书籍为例
class BookAPIView(APIView):
def post(self, request):
info = request.data
ser = BookSerialize(data=request.data)
if ser.is_valid():
return_data = {
'code': 200,
'message': '创建图书成功'
} # 这里并不能正真的保存,还需要做一步处理
return Response(return_data)
return_data = {
'code': 300,
'message': ser.errors # 这里通过errors属性就可以拿到序列化好的错误消息
}
return Response(return_data)
【六】反序列化保存
当我们拿到前端给的数据,并且将这些数据转换成自己的语言能识别的数据并且保存就叫反序列化保存。保存的目的也很明确,拿到前端的数据,经过序列化组件的校验,自然就需要保存到数据库。drf的序列化组件也提供了保存的方法。
需要在自己写的序列化类里面重写create方法,这个方法的validated_data参数是上面通过校验的数据,在方法里面创建一个新的对象到数据库,然后返回新的对象就可以了。
class BookSerialize(serializers.Serializer):
title = serializers.CharField(max_length=8, min_length=3)
price = serializers.IntegerField(max_value=200, min_value=30)
publish = serializers.CharField(max_length=8, min_length=3)
# 重写create方法
# validated_data是通过校验的数据
def create(self, validated_data):
# 需要导入对应的模型表,插入数据
obj = models.Book.objects.create(**validated_data)
# 返回对象
return obj
然后在视图类中直接通过save保存那个serialize对象即可。
class BookAPIView(APIView):
def post(self, request):
info = request.data
ser = BookSerialize(data=request.data)
if ser.is_valid():
# 通过save方法保存
ser.save()
return_data = {
'code': 200,
'message': '创建图书成功'
}
return Response(return_data)
return_data = {
'code': 300,
'message': ser.errors
}
return Response(return_data)
【七】反序列化修改
修改和保存差不多,修改需要重写serialize类的update属性,在视图文件里面需要把需要更新的对象和前端传来的数据传递给serialize对象。它内部通过判断instance是否有值,来判断是修改还是添加。
class BookSerialize(serializers.Serializer):
title = serializers.CharField(max_length=8, min_length=3)
price = serializers.IntegerField(max_value=200, min_value=30)
publish = serializers.CharField(max_length=8, min_length=3)
def update(self, instance, validated_data):
# validated_data是一个字典对象
# 循环拿到键和值
# 再通过setattr反射 将值赋给instance对象
for key, value in validated_data.items():
setattr(instance, key, value)
"""
上面的方法等同于下面这个方法
instance是需要修改的对象
validated_data 是前端传来的数据
instance.title = validated_data.get('title')
instance.price = validated_data.get('price')
instance.publish = validated_data.get('publish')
"""
# save保存
instance.save()
# 最后把这个对象 也就是instance返回即可
return instance
view.py
class BookDetailAPIView(APIView):
def put(self, request, pk):
info = request.data
obj = models.Book.objects.filter(pk=pk).first()
# 将前端传来的数据传入
# 将查找出来的对象传入
ser = BookSerialize(data=request.data, instance=obj)
if ser.is_valid():
# 通过校验后保存
ser.save()
return_data = {
'code': 200,
'message': '修改图书成功'
}
return Response(return_data)