我试图覆盖序列化程序的save()
方法(as per the docs)以支持批量实例创建。目前,我有一些看起来像这样的东西(如果你愿意,可以跳过代码,它只是为了上下文。真正的问题是我无法制作任何自己的序列化方法。)
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
# https://stackoverflow.com/a/36334825/3790954
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
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)
然后我在数据库中获得了新的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
此外,如果我在序列化程序类中创建自己的测试方法:
def test_method(self):
print("Successful test method")
我也不能这样称呼!
>>> serializer.test_method()
AttributeError: 'ListSerializer' object has no attribute 'test_method'
答案 0 :(得分:2)
您的BulkWidgetSerializer
被ListSerializer
包裹,这是DRF的默认行为。这就是为什么你的新方法不见了。
如果使用kwarg BaseSerializer
the library wraps it实例化many=True
的任何子类,并将新ListSerializer
child
设置为Serializer
类。< / p>
因此,您无法覆盖save()
方法以获得所需的效果。
尝试覆盖序列化程序的many_init
类方法,以提供实现所需行为的自定义ListSerializer
{。{3}}。
其次,最好覆盖create()
或update()
方法,而不是调用其中一个方法的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)
然后在BulkWidgetSerializer
:
@classmethod
def many_init(cls, *args, **kwargs):
kwargs['child'] = cls()
return CustomListSerializer(*args, **kwargs)
问题:不要忘记将正确的kwargs从父母传递给孩子,例如kwargs['child'] = cls( partial=kwargs.get('partial') )
如果您在覆盖的方法中依赖孩子班级中的任何一个来支持批量部分更新(例如validate()
)。
答案 1 :(得分:1)
您一直在阅读文档的错误部分,而且您的方法不正确。
多个对象创建的默认实现是简单的 为列表中的每个项调用.create()。如果你想自定义 这种行为,您需要自定义.create()方法 传递many = True时使用的ListSerializer类。
这可以确保您在列表中传递的每个项目都会发生您想要发生的more_business_logic。
根据文档 - http://www.django-rest-framework.org/api-guide/serializers/#customizing-multiple-create
答案 2 :(得分:1)
您似乎正在使用runas /netonly
实例化序列化程序。在这种情况下,many=True
在内部实例化(您可以在类方法rest_framework.serializers.BaseSerializer.many_init
中找到此代码)。
因此调用了ListSerializer
的{{1}}方法。如果必须覆盖save方法,请首先创建自定义列表序列化程序:
save()
然后通过指定ListSerializer
:
class CustomListSerializer(serializers.ListSerializer):
def save(self):
...
BulkWidgetSerializer
根据他人的指定,最好覆盖list_serializer_class
或class Meta:
list_serializer_class = CustomListSerializer
方法,而不是create