Сериализатор для вложенного представления JSONField - PullRequest
1 голос
/ 07 мая 2019

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

Решение, которое работает на данный момент, ноне проверяет, что внутри config_field, слепо принимает все, что соответствует объекту json:

Упрощенная версия моей модели:

class MyModel(models.Model):
    config_field = JSONField(...)
    ...

Ради этоговопрос вот упрощенная версия структуры данных, хранящейся в config_field:

{"some_config_int": 42, "some_config_vars": [{"id": 1}, {"id": 2}]}

А вот упрощенная версия моего сериализатора:

class MyModelSerializer(serializers.ModelSerializer):
        config_field = serializers.JSONField(required=False)
        class Meta:
            model = MyModel
            fields = ('config_field', ...)

Чего бы я хотел достичь, хотядолжен иметь сериализатор для вложенного представления ( ссылка на документацию DRF ) того, что находится внутри config_field.То, что я пробовал до сих пор (но не работает):

class ConfigVarsSerializer(serializers.Serializer):
        id = serializers.IntegerField(required=True)

class ConfigFieldsSerializer(serializers.Serializer):
    some_config_int = serializers.IntegerField(required=True)
    some_config_vars = serializers.ListField(child=ConfigVarsSerializer,required=True)

class MyModelSerializer(serializers.ModelSerializer):
            config_field = ConfigFieldsSerializer(required=False)
            class Meta:
                model = MyModel
                fields = ('config_field', ...)

Таким образом будет необязательно POST / PUT объект с конфигурацией, но если config_field находится в телеВ запросе должен быть указан весь вложенный объект.

Ответы [ 2 ]

1 голос
/ 07 мая 2019

Вы отправляете данные против поля config_field , поэтому ваши данные должны включать этот ключ. Таким образом, полезная нагрузка должна быть такой, как показано ниже

<b>{"config_field": </b>{"some_config_int": 42, "some_config_vars": ["foo", "bar"]}}

Update-1

использовать DictField() в сериализаторе as,

<b>VALID_DICT_KEYS = ['foo_1']</b>


class ConfigFieldsSerializer(serializers.Serializer):
    some_config_int = serializers.IntegerField(required=True)
    some_config_vars = serializers.ListField(<b>child=serializers.DictField()</b>, required=True)

    <b>def validate(self, attrs):
        attrs = super().validate(attrs)
        some_config_vars = attrs['some_config_vars']
        keys_list = []
        for item in some_config_vars:
            keys_list.extend(list(item.keys()))
        unwanted_keys = set(keys_list) - set(VALID_DICT_KEYS)
        if unwanted_keys:
            raise serializers.ValidationError("raise error with some msg")
        return attrs</b>


class MyModelSerializer(serializers.Serializer):
    config_field = ConfigFieldsSerializer(required=False)

    class Meta:
        fields = ('config_field',)


data = {<b>'config_field'</b>: {"some_config_int": 42, "some_config_vars": [{"foo_1": "bar"}, {"foo_2": "honey"}]}}
serializer = MyModelSerializer(data=data)
serializer.is_valid(True)
print(serializer.data)
0 голосов
/ 08 мая 2019

Попробовав несколько возможных решений, я хочу указать на 2 самых простых и наиболее важных из них, которые не требуют переопределения create метода ни для MyModelSerializer, ни для внутренних сериализаторов:

  1. Переопределить метод проверки поля для config_field в MyModelSerializer
  2. Переопределить validate метод для всего объекта, сериализуемого с помощью MyModelSerializer

Сериализаторыпредставление внутреннего содержимого config_field будет одинаковым для обоих решений:

class ConfigVarsSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=True)

class ConfigFieldsSerializer(serializers.Serializer):
    some_config_int = serializers.IntegerField(required=True)
    some_config_vars = serializers.ConfigVarsSerializer(required=True, many=True)

Обратите внимание, что some_config_vars хранит список объектов, поэтому many=True.


Решение 1

Переопределить метод проверки поля для config_field in MyModelSerializer.В случае данного примера конечный код сериализатора будет:

class MyModelSerializer(serializers.ModelSerializer):
        config_field = JSONField(required=False)
        class Meta:
            model = MyModel
            fields = ('config_field', ...)

        def validate_config_field(self, value):
            serializer = ConfigFieldsSerializer(data=value)
            serializer.is_valid(raise_exception=True)
            return value

. Этот подход сначала проверяет config_field, используя значение по умолчанию JSONFieldSerializer, и вызывает исключение, если содержимое не является действительным JSON объектом.

Если JSONFieldSerializer не вызывает исключения, то вызывается validate_custom_fields, и он передает содержимое поля в ConfigFieldsSerializer и проверяет весь контент для себя и всех вложенных сериализаторов.


Решение 2

Переопределить метод validate для всего объекта, сериализуемого с помощью MyModelSerializer.В случае данного примера конечный код сериализатора будет:

class MyModelSerializer(serializers.ModelSerializer):
        config_field = JSONField(required=False)
        class Meta:
            model = MyModel
            fields = ('config_field', ...)

        def validate(self, attrs):
            config_field = attrs.get('config_field')
            if config_field:
                serializer = ConfigFieldsSerializer(data=config_field)
                serializer.is_valid(raise_exception=True)
            return attrs

. Этот подход требует немного больше кода, но позволяет комбинировать проверку config_field с другими связанными полями.

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