字典的update方法

  • 可以修改已存在的键对应的值, 也可以添加新的键-值对到字典中
  • 语法格式: d.update(e)
  • 参数说明: 将e中键-值对添加到字典d中, e可能是字典, 也可能是键-值对序列
  • 返回值: 无
  • 实例: d = {'one': 1}; d.update({'two': 2}); d.update(one='一'); print(d) #

外键深度查询的方式

  1. 子序列化
  2. depth
  3. @property

接口

'''
# ...\d_proj\api\views.py
...
class BookAPIView(APIView):
    ...
    # 单删群删, 将需要被删除的数据的is_delete字段值修改为True 
    """
    1. 单删接口: .../books/(pk)/
    2. 群删接口: .../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:
    		return Response(data={
    			'code': 1,	
                'res': {'detail': '数据有误'},
    		}, status=400)
    	
    	if rows:
    		return Response(data={
    			'code': 0,	
                'res': '删除成功',
    		}, status=200) 
    
    # 单增群增
    def post(self, request, *args, **kwargs):
        """
        1. 单增接口: .../books/, 携带数据: {...}
        2. 群增接口: .../books/, 携带数据: [{...}, ..., {...}]
        """
        try:
            if not request.data:
                raise Exception('数据不能为空')
            if isinstance(request.data, dict):
                many = False
            else:
                many = True
            book_ser = serializers.BookModelSerializer(data=request.data, many=many)
        except Exception as e:
            return Response(data={
                'code': 1,
                'res': {'detail': str(e)},
            }, status=400)

        book_ser.is_valid(raise_exception=True)
        res_book_obj_or_lt = book_ser.save()
        re_book_ser = serializers.BookModelSerializer(instance=res_book_obj_or_lt, many=many)
        return Response(data={
            'code': 0,
            'res': re_book_ser.data,
        }, status=200)
    		
	# 单改群改所有字段: 没有提供修改值的字段沿用原字段值而不是默认值
    def put(self, request, *args, **kwargs):
        """
        1. 单改所有字段接口: .../books/, 携带数据: {pk: ..., ...}
        2. 群改所有字段接口: .../books/, 携带数据: [{pk1: ..., ...}, ..., {pkn: ..., ...}]
        """

        if isinstance(request.data, dict):
            request.data = [request.data]  # 将单改统一成群改的形式

        # 群改所有字段时如果有一条被修改的数据出错, 则直接认为put请求提交的整个数据都有误
        try:
            pks = []
            for dic in request.data:
                pk = dic.pop('pk')  # 字典的pop方法不设置默认值时, 如果被pop的数据在字典中不存在则会直接报错
                pks.append(pk)
            book_queryset = models.Book.objects.filter(is_delete=False, pk__in=pks).all()
            if len(book_queryset) < len(pks):
                raise Exception('部分要修改的数据不存在')

        except Exception as e:
            if isinstance(e, KeyError):
                e = '部分数据缺少pk'
            return Response(data={
                'code': 1,
                'res': {'detail': str(e)},  # e为异常对象, 需要转换成字符串才能返回给前端
            }, status=400)

        book_ser = serializers.BookModelSerializer(instance=book_queryset, data=request.data, many=True)

        book_ser.is_valid(raise_exception=True)
        res_book_queryset = book_ser.save()  # drf框架不实现群改方法的原因: 需要对queryset和request.data提前作一些不可控的处理
        re_book_ser = serializers.BookModelSerializer(instance=res_book_queryset, many=True)
        return Response(data={
            'code': 0,
            'res': re_book_ser.data,
        }, status=200)

	# 单改群该部分字段: 改部分字段兼容改所有字段, 之后只需要写patch方法并在BookModelSerializer类实例化时添加partial=True即可
	def patch(self, request, *args, **kwargs):
		...
		# 通过context在BookAPIView类的方法中给序列化类传参, 在序列化类的方法中可以通过self.context获取从自定义的CBV类中传入的变量
		book_ser = serializers.BookModelSerializer(..., partial=True, context={'request': request})    		
'''

Debug打断点研究many=True时的代码执行流程

'''   
# ...\Lib\site-packages\rest_framework\serializers.py
class BaseSerializer(Field):
	    def __init__(self, instance=None, data=empty, **kwargs):  # ModelSerializer类中没有__init__方法
        self.instance = instance
        ...
        kwargs.pop('many', None)
        super().__init__(**kwargs)
        
    def __new__(cls, *args, **kwargs):
        if kwargs.pop('many', False):
            return cls.many_init(*args, **kwargs)  # cls为BookModelSerializer类
        return super().__new__(cls, *args, **kwargs)
        
    @classmethod
    def many_init(cls, *args, **kwargs):
        ...
        child_serializer = cls(*args, **kwargs)
        list_kwargs = {
            'child': child_serializer,  # child为BookModelSerializer类的对象
        }
        ...
        meta = getattr(cls, 'Meta', None)
        list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)  
        return list_serializer_class(*args, **list_kwargs)  # 当many=True时, 返回list_serializer_class配置的类的对象


class ListSerializer(BaseSerializer):
    ...
    def __init__(self, *args, **kwargs):  # self为list_serializer_class配置的类的对象
        self.child = kwargs.pop('child', copy.deepcopy(self.child))  # child为BookModelSerializer类的对象child属性
        ...
        
	...    
    def update(self, instance, validated_data):  # 群改方法需要仿照群增方法自己实现
        raise NotImplementedError(
            ...
        )

    def create(self, validated_data):
        return [
            self.child.create(attrs) for attrs in validated_data
        ]
        
    def save(self, **kwargs):
        ...
        if self.instance is not None:
            self.instance = self.update(self.instance, validated_data)
            ...
        else:
            self.instance = self.create(validated_data)
            ...
        return self.instance
        
        
# ...\d_proj\api\my_serializers.py
...
class BookListSerializer(serializers.ListSerializer):
    def update(self, instance, validated_data):
        return [
            self.child.update(instance[index], attrs) for index, attrs in enumerate(validated_data)
        ]


class BookModelSerializer(serializers.ModelSerializer):
	# ...\d_proj\api\models.py: name = models.CharField(max_length=64, unique=True)
	name = serializers.CharField(max_length=64)  # 需要自定义name字段的反序列化校验规则, 否则会按照model表中的规则校验unique=True, 导致报错
	
    class Meta:
        list_serializer_class = BookListSerializer
        ...
'''

一个花了很多时间的bug

'''
# ...\d_proj\api\my_serializers.py
...
class BookModelSerializer(serializers.ModelSerializer):
	# ...\d_proj\api\models.py: name = models.CharField(max_length=64, unique=True)
	name = serializers.CharField(max_length=64)  # 需要自定义name字段的反序列化校验规则, 否则会按照model表中的规则校验unique=True, 导致报错
    ...
'''