Поле JSON иногда содержит строку, иногда объект

Я использую Django rest framework для валидации JSON, полученного от API, который принимает финансовые транзакции. Сериализатор работает уже несколько лет. Поле merchant в JSON всегда содержало продавца в виде вложенного объекта с ID продавца, именем и т.д. Но теперь я иногда получаю только ID продавца в виде строки, и JSON теперь не проходит валидацию.

Как настроить сериализатор, чтобы в поле merchant можно было указать либо строку, либо объект?

В основном поля торговца должны принимать одно из этих полей:

merchant = MerchantSerializer(required=False, allow_null=True)
merchant = serializers.CharField(required=False, max_length=50)

serializers.py

class MerchantSerializer(serializers.Serializer):
    id = serializers.CharField(required=True, max_length=50)
    name = serializers.CharField(required=True, max_length=100)
    atm = serializers.BooleanField(required=False, allow_null=True)
    address = AddressSerializer(required=False, allow_null=True)
    logo = serializers.URLField(required=False, allow_null=True, max_length=500, min_length=None, allow_blank=True)

class DataSerializer(serializers.Serializer):
    account_id = serializers.CharField(required=True, max_length=50)
    amount = serializers.IntegerField(required=True)
    created = serializers.DateTimeField()
    currency = serializers.CharField(required=True, max_length=3)
    description = serializers.CharField(required=True, max_length=250)
    id = serializers.CharField(required=True, max_length=50)
    category = serializers.CharField(required=True, max_length=100)
    decline_reason = serializers.CharField(required=False, allow_null=True, allow_blank=True, max_length=100)
    merchant = MerchantSerializer(required=False, allow_null=True)
    counterparty = CounterpartySerializer(required=False, allow_null=True)
    metadata = MetadataSerializer(required=False, allow_null=True)

Для поля required необходимо установить значение false.name

class MerchantSerializer(serializers.Serializer):
    id = serializers.CharField(required=True, max_length=50)
    name = serializers.CharField(required=False, max_length=100)   # this field
    atm = serializers.BooleanField(required=False, allow_null=True)
    address = AddressSerializer(required=False, allow_null=True)
    logo = serializers.URLField(required=False, allow_null=True, max_length=500, min_length=None, allow_blank=True)

Надеюсь, это может помочь.

Вы можете переопределить to_internal_value таким образом, чтобы он пытался разобрать переданные данные. В приведенном ниже решении он просто проверяет, являются ли данные словарем, и устанавливает их в словарь, если это не так.

Это решение позволит сериализатору принимать строку, но внутри сериализатора она станет словарем, содержащим только ключ id.

class MerchantSerializer(serializers.Serializer):
    id = serializers.CharField(required=True, max_length=50)
    # other fields ...

    def to_internal_value(self, data):
        parsed_data = data
        if not isinstnace(parsed_data, dict):
            parsed_data = { "id": data }
        return super().to_internal_value(parsed_data)

Вероятно, вам придется изменить некоторые валидации в сериализаторе, чтобы он мог принимать только id, хотя.

Вернуться на верх