有没有办法修改 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 ,您可以在编写自己的字段时复制或引用它。我已经包含了一些要点的片段,但由于它不是我的,所以我不愿意复制整个内容。
# 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
您可以像这样创建客户序列化器:
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)