我正在使用ListSerializer
一次处理多个对象。它接受对象的JSON数组,即request.data
是Python端list
中的dict
个。
在我的ListCreateAPIView
中,我正在考虑重写get_object()
,以便获取在PATCH
或PUT
请求中修改的对象。我计划通过遍历request.data
列表中给出的对象并从数据库中获取它们来实现此目的。
(请注意,对于单对象请求,通用工作流使用基于URL的查找字段来获取实例。但是,您无法在URL中标识多个对象。)
不幸的是,这种方法不能正确处理request.data
输入无效的情况,例如当它不是列表时。 这是问题的根源。
那么,如何处理呢?这里有一些想法:
在get_object()
视图方法中执行额外的验证步骤。
在调用instance
之前,不要在序列化器中填充serializer.save()
,并修改save()
方法,以便提取现有实例并将其传递给update()
:
### in the serializer's save() method
instance = get_instances_somehow(validated_data) # not based on request.data
if self.instance is not None:
# in the generic workflow, this passes the pre-populated self.instance
self.instance = self.update(instance, validated_data)
else:
self.instance = self.create(validated_data)
return self.instance
### in the view's update() method
serializer = self.get_serializer(None, data=request.data, many=True)
serializer.is_valid(raise_exception=True)
serializer.instance = self.get_object() # this line differs from generic workflow
self.perform_update(serializer)
注意事项:
这种方法看起来很丑陋,因为使用is_valid()
初始化的序列化程序的many=True
方法已经进行了此检查。此外,验证不是视图的任务。不幸的是,is_valid()
仅在通用工作流程中视图的get_object()
方法之后才被调用,因此get_object()
无法访问经过验证的数据。
这会将实例的获取从视图的get_object()
方法移至序列化程序。由于这也破坏了DRF的概念结构,因此也不是理想的解决方案。
这似乎最有希望。
但是,方法2)和方法3)还有另一个问题:串行器的data
属性的返回值取决于self.instance
是否为None
。换句话说,通过稍后设置序列化程序的instance
属性,data
属性的用户可能会获得与其他情况不同的数据。 (如果存在self.instance
,则data
对应于它,否则对应于validated_data
。)
解决此问题的最佳方法是什么?
奖励问题:有时候,验证取决于实例是被修改还是被创建。例如,如果实例具有一次写入字段,其值是在创建时提供的,以后无法更改,则可能发生这种情况。假定该字段不是主键,因此,如果不存在任何实例,则应仅将该字段视为“必需”。这似乎是将实例尽快移交给序列化程序的一个很好的理由。但是,如果需要访问request.data
,该怎么办呢?如果不先调用is_valid()
,就不能直接访问它?
答案 0 :(得分:0)
我最终得到了以下解决方案:
1。)返回视图的get_object()
中的过滤查询集:
### view's get_object() method:
def get_object(self):
return self.filter_queryset(self.get_queryset())
根据DRF文档it is acceptable to serialize a queryset。
2。)在视图中调用的序列化程序的构造函数将其保存到self.instance
,然后将其作为update()
参数传递给instance
方法。此方法也会被传递validated_data
。我们可以使用后者对前者进行一些过滤:
### serializer's update() method:
def update(self, instance, validated_data):
instance = instance.filter(...) # based on validated_data
# proceed with your logic
这是受以下观察启发的:DRF docs section on multiple updates也循环遍历validated_data
,然后从instance
中选择可能是更大集合的项目,就像这里的查询集一样。
使用这种方法,我们可以在初始化序列化程序时传递一些合理的实例作为实例,但是在验证运行后对实例列表进行详细过滤。
注意:有人可能会认为将较大的查询集传递给序列化程序效率低下。但是,直到您对instance
方法中的过滤后的update()
执行某些操作后,才会对queryset进行评估。换句话说,最后只会得到一个数据库查询,该查询将应用所有过滤条件。