Передача partial=True во вложенный сериализатор в DRF
У меня есть два сериализатора, организованных следующим образом:
class OuterSerializer():
inner_obj = InnerSerializer(many=True, required=False)
other fields ......
class InnerSerializer():
field_1 = CharField()
field_2 = CharField()
Теперь мне нужно частично обновить модель внешнего сериализатора. Я делаю это следующим образом:
def partial_update(self, request, *args, **kwargs):
serializer = OuterSerializer(data=request.data, context={'request': self.request}, partial=True)
serializer.is_valid(raise_exception=True)
data = serializer.data
outerobj = self.service_layer.update(kwargs['pk'], data, request.user)
response_serializer = OpportunitySerializer(instance=outerobj, context={'request': self.request})
return Response(response_serializer.data, HTTPStatus.OK)
Проблема в том, что этот частичный флаг не передается в InnerSerializer. Например, если тело моего запроса выглядит как показано ниже, я хочу, чтобы оно работало:
{"inner_obj":
{
"field_1" : "abc"
}
}
В настоящее время я получаю 400 ошибку, говорящую о том, что поле является обязательным.
Что я пробовал :
- Установка частичной переменной внутри OuterSerializer в методе init, изменив его таким образом
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# We pass the "current serializer" context to the "nested one"
self.fields['inner_obj'].context.update(self.context)
self.fields['inner_obj'].partial = kwargs.get('partial')
Однако это не распространяется вниз.
Попробуйте изменить InnerSerializer
так, чтобы он мог принимать аргумент partial
и передавать его своему родителю, например, следующим образом:
class InnerSerializer(serializers.Serializer):
field_1 = CharField()
field_2 = CharField()
def __init__(self, *args, **kwargs):
self.partial = kwargs.pop('partial', False)
super().__init__(*args, **kwargs)
class OuterSerializer(serializers.Serializer):
inner_obj = InnerSerializer(many=True, required=False)
other fields ......
def __init__(self, *args, **kwargs):
partial = kwargs.get('partial')
super().__init__(*args, **kwargs)
self.fields['inner_obj'].child.partial = partial
Еще одно возможное решение.
Вы также можете переопределить метод to_internal_value()
в InnerSerializer
, чтобы заставить его принимать обновления partial
так:
class InnerSerializer(serializers.Serializer):
field_1 = CharField()
field_2 = CharField()
def to_internal_value(self, data):
if self.partial:
return {field: data.get(field, getattr(self.instance, field)) for field in data}
return super().to_internal_value(data)
class OuterSerializer(serializers.Serializer):
inner_obj = InnerSerializer(many=True, required=False)
other fields ......
Редактирование:
Для ошибки:
KeyError: "Получена ошибка KeyError при попытке получить значение для поля field_2
on serializer
InnerSerializer`.
Сообщение об ошибке, с которым вы столкнулись, предполагает, что сериализатор пытается получить доступ к значению field_2
из данных, но оно отсутствует.
В настоящее время для решения ошибки необходимо переопределить метод to_representation()
в InnerSerializer
, чтобы включить только те поля, которые присутствуют, таким образом:
class InnerSerializer(serializers.Serializer):
field_1 = CharField()
field_2 = CharField()
def to_representation(self, instance):
data = super().to_representation(instance)
return {field: value for field, value in data.items() if value is not None}