十大接口与视图家族
1、序列化类外键字段的覆盖
1、在序列化类中自定义字段,名字与model类中属性名一致,称之为覆盖(覆盖的是属性的所有规则:extra_kwargs中的、model字段提供的默认的、数据库唯一约束等校验规则)
2、外键覆盖字段用PrimaryKeyRelatedField来实现,可以做到只读、只写、可读可写三种
-
只读:read_only = True
-
只写:queryset = 关联表的queryset,write_only
-
可读可写:queryset = 关联表的queryset
3、当外界关联的数据有多个时,需要标识many = True
class BookModelSerializer(serializers.ModelSerializer): # 如何覆盖外键字段 # publish = serializers.PrimaryKeyRelatedField(read_only=True) # 只读 # publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all(), write_only=True) # 只写 # publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all()) # 可读可写 publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all()) authors = serializers.PrimaryKeyRelatedField(queryset=models.Author.objects.all(), many=True) class Meta: model = models.Book fields = ('name', 'price', 'image', 'publish', 'authors')
2、十大接口序列化
# Baseserializer类中各参数含义 def __init__(self, instance=None, data=empty, **kwargs): pass
-
instance:对象类型赋值给instance
-
data:请求来的数据赋值给data
-
kwargs:内部有三个属性:many、partial、context
-
many:操作对象或数据时有多个时many=True
-
partial:在修改需求时校验所有的字段required=False时使用partial=True
-
context:用于视图类和序列化类直接传参使用
-
1、初始化序列化类,设置partial = True可以将所有的反序列化字段required设置为False(提供就校验,不提供就不校验),可以在局部修改接口的时候使用
2、初始化序列化类,设置context = {...},在序列化类操作self.context,实现视图类和序列化类数据互通
3、只有要完成资源群改这种需求的时候,才需要自定义ListSerializer绑定给自定义的ModelSerializer,重写update方法来实现需求
六个必备接口:单查、群查、单增、单删、单整体改、单局部改
四个额外接口(了解):群增、群删、群整体改、群局部改
1、单查、群查
单查接口:/books/pk/
群查接口:/books/
class BookAPIView(APIView): def get(self, request, *args, **kwargs): pk = kwargs.get('pk') # 单查,未删除的 if pk: obj = models.Book.object.filter(is_delete=False,pk=pk).first() serializer = serializers.BookModelSerializer(instance=obj) # 此处的APIResponse是自定义的二次封装的Response return APIResponse(result=serializer.data) # 群查 else: queryset = models.Book.object.filter(is_delete=False).all() serializer = serializers.BookModelSerializer(instance=queryset, many=True) return APIResponse(results=serializer.data)
2、单增、群增
单增接口:/books/ 数据:dict
群增接口:/books/ 数据:list
区分是单增还是群增就是判断request.data是dict还是list
def post(self, request, *args, **kwargs): # 单增 if not isinstance(request.data, list): serialiazer = serializer.BookModelSerializer(data=request.data) serializer.is_valid(raise_exception=True) # 校验失败直接抛异常 obj = serialiazer.save() # 存入数据库中 return APIResponse(result=serializer.BookModelSerializer(obj).data, http_status=201) else: # 群增 serialiazer = serializer.BookModelSerializer(data=request.data, many=True) serializer.is_valid(raise_exception=True) # 校验失败直接抛异常 objs = serialiazer.save() # 存入数据库中 return APIResponse(result=serializer.BookModelSerializer(objs, many=True).data, http_status=201)
3、单删、群删
单删群删接口不需要利用序列化,可以直接操作数据库
单删接口:/books/pk/
群删接口:/books/ 数据:[pk1, ...., pkn]
def delete(self, request, *args, **kwargs): pk = kwargs.get('pk') if pk: pks = [pk] # 单删伪装成群删一条 else: pks = request.data # 群删的数据就是群删的主键们 try: rows = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True) except: # 如果传的request.data提交的数据乱七八糟的,ORM操作就会异常 return APIResponse(1, '数据有误') if rows: # 受影响行数,只要有就删除成功 return APIResponse(0, '删除成功') # 如果传的都是已经删除过的,没有受影响行数就会删除失败 return APIResponse(2, '删除失败')
4、单整体改、群整体改
单整体改接口:/books/pk/ 数据:dict
群整体改接口:/books/ 数据:[{pk:1, ...}, ..., {pk:n, ...}] 或者 {pks: [pk1, ..., pkn], data: [{}, ..., {}]}一般都用第一种
def put(self, request, *args, **kwargs): pk = kwargs.get('pk') if pk: # 单 try: instance = models.Book.objects.get(is_delete=False, pk=pk) except: return APIResponse(1, 'pk error', http_status=400) # 序列化类同时赋值instance和data,代表用data重新更新instance => 修改 serializer = serializers.BookModelSerializer(instance=instance, data=request.data) serializer.is_valid(raise_exception=True) obj = serializer.save() return APIResponse(result=serializers.BookModelSerializer(obj).data) else: # 群 """ 分析request.data数据 [{'pk':1, 'name': '', 'publish': 1, 'authors': [1, 2]}, ...] 1)从 request.data 中分离出 pks 列表 2)pks中存放的pk在数据库中没有对应数据,或者对应的数据已经被删除了,这些不合理的pk要被剔除 3)pks最终转换得到的 objs 列表长度与 request.data 列表长度不一致,就是数据有误 """ pks = [] try: # 只要不是要求的标准数据,一定会在下方四行代码某一行抛出异常 for dic in request.data: pks.append(dic.pop('pk')) objs = models.Book.objects.filter(is_delete=False, pk__in=pks) assert len(objs) == len(request.data) # 两个列表长度必须一致 except: return APIResponse(1, '数据有误', http_status=400) serializer = serializers.BookModelSerializer(instance=objs, data=request.data, many=True) serializer.is_valid(raise_exception=True) objs = serializer.save() return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data)
5、单局部改、群局部改
跟单整体改和群整体改一样,只不过加了一个partial=True
单局部改接口:/books/pk/ 数据:dict
群局部改接口: /books/ 数据:[{pk:1, ...}, ..., {pk:n, ...}] 或者 {pks: [pk1, ..., pkn], data: [{}, ..., {}]}一般都用第一种
def put(self, request, *args, **kwargs): pk = kwargs.get('pk') if pk: # 单 try: instance = models.Book.objects.get(is_delete=False, pk=pk) except: return APIResponse(1, 'pk error', http_status=400) # 序列化类同时赋值instance和data,代表用data重新更新instance => 修改 serializer = serializers.BookModelSerializer(instance=instance, data=request.data) serializer.is_valid(raise_exception=True) obj = serializer.save() return APIResponse(result=serializers.BookModelSerializer(obj).data) else: # 群 """ 分析request.data数据 [{'pk':1, 'name': '', 'publish': 1, 'authors': [1, 2]}, ...] 1)从 request.data 中分离出 pks 列表 2)pks中存放的pk在数据库中没有对应数据,或者对应的数据已经被删除了,这些不合理的pk要被剔除 3)pks最终转换得到的 objs 列表长度与 request.data 列表长度不一致,就是数据有误 """ pks = [] try: # 只要不是要求的标准数据,一定会在下方四行代码某一行抛出异常 for dic in request.data: pks.append(dic.pop('pk')) objs = models.Book.objects.filter(is_delete=False, pk__in=pks) assert len(objs) == len(request.data) # 两个列表长度必须一致 except: return APIResponse(1, '数据有误', http_status=400) serializer = serializers.BookModelSerializer(instance=objs, data=request.data, many=True) serializer.is_valid(raise_exception=True) objs = serializer.save() return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data)
3、视图家族
视图基类:APIView、GenericAPIView
视图工具类:mixins包下的五个类(六个方法)
工具视图类:generics包下的所有GenericAPIView的子类
视图集:viewsets包下的类
1、GenericAPIView基类
基本不会单独使用,是高级视图类的依赖基础
-
GenericAPIView继承了APIView所有的APIView子类写法再继承GenericAPIView时可以保存一致
-
GenericAPIView给我们提供了三个属性queryset、serializer_class、lookup_field
-
GenericAPIView给我们提供了三个方法get_queryset、get_serializer、get_obj
2、mixins包存放了视图工具类(不能单独使用,必须配合GenericAPIView使用)
CreateModelMixin:单增工具类 create方法
RetrieveModelMixin:单查工具类 retrieve方法
ListModelMixin:群查工具类 list方法
UpdateModelMixin:单整体局部改工具类 update方法
DestroyModelMixin:单删工具类 destory方法
3、generic包下的所有GenericAPIView的子类
就是继承GenericAPIView和不同的mixins下的工具类的组合
1、定义的视图类,继承generic包下已有的特点GenericAPIView子类,可以在只初始化queryset和serializer_class两个类属性后,就获取特点的功能
2、定义的视图类,自己继承GenericAPIView基类,再任意组合mixins包下的一个或多个工具类,可以实现自定义的工具视图类,获取特点的功能或功能们
注意:
-
在这些模式下,不能实现单查和群查共存,但是可以加逻辑区分,也可以用视图集
-
# ----------------------------- 过渡写法:了解 ----------------------------- from rest_framework.generics import GenericAPIView class BookV1APIView(GenericAPIView): # 将数据和序列化提升为类属性,所有的请求方法都可以复用 queryset = models.Book.objects.filter(is_delete=False).all() # ORM优化机制一次只能取出21条数据 serializer_class = serializers.BookModelSerializer lookup_field = 'pk' # 可以省略,默认是pk,与url有名分组对应的 # 群查 def get(self, request, *args, **kwargs): # queryset = models.Book.objects.filter(is_delete=False).all() # => 方法+属性两行代码 queryset = self.get_queryset() # serializer = serializers.BookModelSerializer(instance=queryset, many=True) # => 方法+属性两行代码 serializer = self.get_serializer(instance=queryset, many=True) return APIResponse(results=serializer.data) # 单查 # def get(self, request, *args, **kwargs): # obj = self.get_object() # serializer = self.get_serializer(obj) # return APIResponse(results=serializer.data) # 单增 def post(self, request, *args, **kwargs): # serializer = serializers.BookModelSerializer(data=request.data) serializer = self.get_serializer(data=request.data) # 同样的步骤多了,好处就来了 serializer.is_valid(raise_exception=True) obj = serializer.save() return APIResponse(result=self.get_serializer(obj).data, http_status=201) # ----------------------------- 过渡写法:了解 ----------------------------- from rest_framework.generics import GenericAPIView from rest_framework import mixins class BookV2APIView(GenericAPIView, mixins.RetrieveModelMixin, mixins.CreateModelMixin): queryset = models.Book.objects.filter(is_delete=False).all() serializer_class = serializers.BookModelSerializer # 单查 def get(self, request, *args, **kwargs): # obj = self.get_object() # serializer = self.get_serializer(obj) # return APIResponse(results=serializer.data) # return self.retrieve(request, *args, **kwargs) response = self.retrieve(request, *args, **kwargs) return APIResponse(result=response.data) # 单增 def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) # ----------------------------- 开发写法:常用 ----------------------------- from rest_framework.generics import RetrieveAPIView class BookV3APIView(RetrieveAPIView): queryset = models.Book.objects.filter(is_delete=False).all() serializer_class = serializers.BookModelSerializer # 单查 pass