Сериализатор 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