DRF 序列化器将逗号分隔的字符串解析为列表字段

问题描述 投票:0回答:2

有没有办法修改 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)
python django django-rest-framework
2个回答
2
投票

ListField
将适用于json,或多值查询字符串或表单主体(如下所示)。它不解析逗号分隔的字符串。

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

您需要的是一个实现解析逻辑的自定义字段:接受一个字符串并使用分隔符(

,
:
等)分割它,然后使用子字段验证它。

没有以这种方式工作的内置字段,但是这里有一个很棒的 示例 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

0
投票

您可以像这样创建客户序列化器:

class CharacterSeparateListField(serializers.ListField):

    def __init__(self, *args, **kwargs):
        self.separator = kwargs.pop("separator", ",")
        self.return_type = kwargs.pop("return_type", int)
        super().__init__(*args, **kwargs)

    def to_representation(self, value: str) -> List[Union[str, int]]:
        update_value = value.split(self.separator)
        if self.return_type == int:
            return [int(i) for i in update_value]
        return update_value

并在序列化器中使用:

class SearchUserSerializer(serializers.Serializer):
    search = CharacterSeparatedField(separator=" ", return_type=str, required=False)
© www.soinside.com 2019 - 2024. All rights reserved.