Вызвать метод .update () вложенного Serializer - PullRequest
1 голос
/ 16 апреля 2019

В моей модели JSONField, в котором хранятся некоторые данные конфигурации.Я хочу получить доступ к этому полю (как чтение, так и запись) с возможностью частичного обновления внутренних полей и их значений.

В целях примера пусть модель будет называться MyModel, а JSONField называется config:

class MyModel(models.Model):
    config = JSONField(default=dict())
    ...

Я создал отдельный ViewSet для доступа к информации, хранящейся в config поле.Предположим, что модель user имеет отношение ForeignKey к MyModel.Упрощенная версия этого ViewSet:

class ConfigurationFieldViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet):

serializer_class = MyModelConfigurationSerializer

def get_object(self):
    return self.request.user.my_model

Данные, хранящиеся в config, имеют определенную структуру с несколькими возможными внутренними объектами:

{
    "C1": {"counter": 42, "active": false},
    "C2": {"counter": 13, "active": true}
}

Для доступа и правильной сериализации MyModel Экземпляр на всех уровнях вложенности. Я создал сериализаторы для каждого уровня поля.Для доступа к полю config в самом MyModel я использую этот сериализатор:

class MyModelConfigurationSerializer(serializers.ModelSerializer):
    configuration = ConfigurationFieldSerializer(required=True)

    class Meta:
        model = MyModel
        fields = ('configuration',)

Для доступа и сериализации первого слоя поля configuration есть второй сериализатор:

class ConfigurationFieldSerializer(serializers.Serializer):
    C1 = BaseConfigurationSerializer(required=True)
    C2 = BaseConfigurationSerializer(required=True)

Наконец, для доступа к внутренней структуре каждого поля C1 и C2 существует третий сериализатор:

class BaseConfigurationSerializer(serializers.Serializer):

    counter = serializers.IntegerField(
        required=False,
        help_text=_('Some integer field help text')
    )
    active = serializers.BooleanField(
        required=False,
        help_text=_('Some boolean field description')
    )

Приведенный выше код отлично работает для чтения данных, хранящихся в поле config, и корректной сериализации его внутренних объектов.,Проблема возникает, когда я пытаюсь выполнить PUT в этом поле.

Если я переопределяю update метод на уровне MyModelConfigurationSerializer, то сериализаторы проверяют данные, которые я отправляю, но в виде чанка иЯ могу сохранить все сразу.Если я пытаюсь передать какое-то внутреннее поле, я все еще правильно получаю ошибки проверки внутренними сериализаторами.

    def update(self, instance, validated_data):
        instance.configuration = validated_data.get(
            'configuration', instance.configuration
        )
        instance.save()
        return instance

Что я не могу сделать, так это вызвать update методы внутренних сериализаторов (ConfigurationFieldSerializerи BaseConfigurationSerializer в этом случае): если я реализую их update методы, они просто не будут вызваны.

Согласно Документация DRF возможны записываемые вложенные представления и соответствуют updateили create методы должны вызываться всякий раз, когда update вызывается на сериализаторе верхнего уровня.

1 Ответ

0 голосов
/ 16 апреля 2019

У меня тоже недавно была эта проблема, и похоже, что то, как вы это делаете, является «единственным способом», когда речь идет о вложенных записываемых сериализаторах.

Из тех же документов DRF , которые вы, вероятно, уже видели:

Поскольку поведение вложенных созданий и обновлений может быть неоднозначным и может требовать сложных зависимостей между связанными моделями, среда REST 3 требует, чтобы вы всегда писали эти методы явно. Методы ModelCerializer .create () и .update () по умолчанию не включают поддержку вложенных представлений с возможностью записи.

Тем не менее, доступны сторонние пакеты, такие как DRF Writable Nested, которые поддерживают автоматические записываемые вложенные представления.

По сути, это означает, что когда у вас есть вложенность, он даже не будет пытаться вызвать какой-либо из вложенных методов хранения сериализатора.

Это может показаться немного болезненным, но, оглядываясь назад, это, вероятно, лучше для дизайна вашего приложения. Ваш пример довольно прост, но в других ситуациях порядок сохранения вещей может быть важен. Если бы update каждого вложенного сериализатора запускался автоматически, тогда DRF должен был бы каким-то образом знать, когда сохранять каждую вещь.

Например, если ваш пример был примерно create, а не update, это означало бы, что вам нужно сначала сохранить вашу модель MyModel, прежде чем сохранять конфигурацию поверх нее. Однако DRF не может этого знать.

Также возможно, что конфигурация на самом деле была другой связанной моделью, которую необходимо сохранить сначала , прежде чем вы сможете сохранить отношение к ней из MyModel. Таким образом, DRF выбирает путь, который говорит вам сделать это самостоятельно, у корневого сериализатора.

Исходя из моего собственного опыта, это также полезно для более точной настройки производительности позже (например, в вашем случае вы можете избежать экономии MyModel дважды).

Наконец, если вы хотите сделать свой код более модульным, вы все равно можете это сделать (отправить сегменты проверенных данных различным обработчикам, например, в новую функцию update_configurations()), это просто не будет выполнено автоматически с помощью вложенные сериализаторы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...