Сериализатор DRF разбирает строку, разделенную запятыми, в поле списка

Существует ли способ изменить то, как сериализаторы DRF разбирают полезную нагрузку входящего запроса?

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

Что я сейчас делаю

class ExampleSerializer(...):
    list_field = serialzers.ListField(child=serializers.Integerfield(...))
    # more fields

def view(request):
    payload = request.GET
    payload["list_field"] = str(payload.get("list_field", "")).split(",")
    serializer = ExampleSerializer(data=payload)

Что бы я предпочел (используя тот же сериализатор, что и выше)

def view(request):
   serializer = ExampleSerializer(data=request.GET)

ListField будет работать с json, или с многозначными строками запроса или телами форм (как показано ниже). Он не разбирает строки, разделенные запятыми.

This will work:
GET /path/?list_field=1&list_field=2&list_field=3

Вам нужно пользовательское поле, которое реализует вашу логику парсинга: примите строку и разделите ее с помощью разделителя (,, или :, и т.д.), а затем проверьте ее с помощью дочернего поля.

Не существует встроенного поля, которое работает таким образом, но есть отличный пример GIST здесь, который вы можете скопировать или использовать при написании собственного поля. Я включил несколько фрагментов из gist, но поскольку он не мой, мне не удобно копировать его целиком.

# https://gist.github.com/imomaliev/77fdfd0ab5f1b324f4e496768534737e

class CharacterSeparatedField(serializers.ListField):
    def __init__(self, *args, **kwargs):
        self.separator = kwargs.pop("separator", ",")
        super().__init__(*args, **kwargs)

    def to_internal_value(self, data):
        data = data.split(self.separator)
        return super().to_internal_value(data)

    # continues ...

class TestCharacterSeparatedManyField:
    def test_field_from_native_should_return_list_for_given_str(self):
        field = CharacterSeparatedField(child=serializers.CharField())
        assert field.to_internal_value("a,b,c") == ["a", "b", "c"]

Вы также можете написать пользовательскую функцию validate_{fieldname} для изменения значения. Это, по крайней мере, сохранит его в сериализаторе. Однако, если возможно, лучше использовать соответствующее поле, но это обычная схема для одноразовой валидации/трансформации, подобной этой.

class ExampleSerializer(Serializer):
    list_field = CharField()

    def validate_list_field(self, value):
        arr = value.split(",")
        arr = [int(x) for x in arr if x.isdigit()]
        if len(arr) == 0:
            raise ValidationError("Supply at least 1 value.")
        return arr
Вернуться на верх