Esc*_*her 1 python django django-rest-framework
我正在尝试覆盖我的序列化程序的save()方法(根据文档)以支持批量实例创建。目前,我有一些看起来像这样的东西(如果你愿意,可以跳过代码,这只是为了上下文。真正的问题是我不能制作任何我自己的序列化方法)。
class BulkWidgetSerializer(serializers.ModelSerializer):
""" Serialize the Widget data """
#http://stackoverflow.com/questions/28200485/
some_foreign_key = serializers.CharField(source='fk_fizzbuzz.name', read_only=False)
class Meta:
model = Widget
fields = (
'some_foreign_key',
'uuid',
'foobar',
)
# Normally we would set uuid to read_only, but then it won't be available in the self.validate()
# method. We also need to take the validator off this field to remove the UNIQUE constraint, and
# perform the validation ourselves.
# See https://github.com/encode/django-rest-framework/issues/2996 and
# /sf/answers/2543437781/
extra_kwargs = {
'uuid': {'read_only': False, 'validators': []},
}
def validate(self, data):
return super(WidgetSerializer, self).validate(self.business_logic(data))
def save(self):
print("---------Calling save-----------")
more_business_logic()
instances = []
for widget in self.validated_data:
instances.append(Widget(**self.validated_data))
Widget.objects.bulk_create(instances)
return instances
Run Code Online (Sandbox Code Playgroud)
class WidgetViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = BulkWidgetSerializer
pagination_class = WidgetViewSetPagination
lookup_field = 'uuid'
def partial_update(self, request):
serializer = self.get_serializer(data=request.data,
many=isinstance(request.data, list),
partial=True)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
pdb.set_trace()
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
Run Code Online (Sandbox Code Playgroud)
然后我在数据库中获得了新的 Wiget 实例,但它们的属性表明没有发生more_business_logic()调用save()。但是,我确实收到反馈,表明发生business_logic了validate()通话。
我由此推测,我不知何故仍然被super班级的save()? 如何覆盖此方法?
当我在两个文件中重命名save()为newsave(),并尝试在 ViewSet 中调用它时,我得到:
AttributeError: 'ListSerializer' 对象没有属性 'newsave'
这是怎么回事?pdb在断点处检查显示它确实是一个BulkWidgetSerializer. 在 shell 中检查显示newsave绝对是该类的方法:
>>>'newsave' in [func for func in dir(BulkWidgetSerializer) if callable(getattr(BulkWidgetSerializer, func))]
True
Run Code Online (Sandbox Code Playgroud)
此外,如果我在序列化程序类中创建自己的测试方法:
def test_method(self):
print("Successful test method")
Run Code Online (Sandbox Code Playgroud)
我也不能这么叫!
>>> serializer.test_method()
AttributeError: 'ListSerializer' object has no attribute 'test_method'
Run Code Online (Sandbox Code Playgroud)
您BulkWidgetSerializer的包装ListSerializer是 DRF 的默认行为。这就是为什么你的新方法不见了。
如果您实例化BaseSerializerwith kwargmany=True 的任何子类,库会将其包装为 new ListSerializerwith childset 到您的Serializer类。
因此,您无法覆盖save()方法以获得所需的效果。尝试覆盖many_init序列化程序的 classmethod 以提供ListSerializer实现所需行为的自定义,如 DRF 文档中所示。
其次,最好重写create()orupdate()方法,而不是save()调用其中之一。
您的实现可能如下所示:
class CustomListSerializer(serializers.ListSerializer):
def create(self, validated_data):
more_business_logic()
instances = [
Widget(**attrs) for attrs in validated_data
]
return Widget.objects.bulk_create(instances)
Run Code Online (Sandbox Code Playgroud)
然后在BulkWidgetSerializer:
@classmethod
def many_init(cls, *args, **kwargs):
kwargs['child'] = cls()
return CustomListSerializer(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
一个问题:不要忘记将正确的 kwargs 从父级传递给子级,例如,kwargs['child'] = cls( partial=kwargs.get('partial') )如果在覆盖方法期间依赖子类中的任何一个kwargs来支持批量部分更新(例如validate())。
看来您正在使用 实例化您的序列化器many=True。在这种情况下,ListSerializer是在内部实例化的(您可以在类方法中找到它的代码rest_framework.serializers.BaseSerializer.many_init)。
因此称为save()的方法。ListSerializer如果必须重写 save 方法,请首先创建一个自定义列表序列化器:
class CustomListSerializer(serializers.ListSerializer):
def save(self):
...
Run Code Online (Sandbox Code Playgroud)
BulkWidgetSerializer然后通过指定以下内容将此自定义列表序列化器添加到您的列表中list_serializer_class:
class Meta:
list_serializer_class = CustomListSerializer
Run Code Online (Sandbox Code Playgroud)
正如其他人所指定的,最好重写 或create方法update而不是save
| 归档时间: |
|
| 查看次数: |
5374 次 |
| 最近记录: |