Django REST Framework - 自定义序列化程序覆盖模型约束

Django REST Framework - Custom serializer overrides model constraints

我的目标是将来自 Django Rest Framework 中用户负载的特定字段中的所有字母大写。

这里是模型:

class Order(models.Model):
    class Item(models.TextChoices):
        FIRST= 'FIRST', _('FIRST')
        SECOND= 'SECOND', _('SECOND')
    ...
    order_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
    the_item = models.CharField(max_length=8, choices=Item.choices)

唯一允许的值是大写的“FIRST”和“SECOND”。

我使用了this方法来将字母大写:

在 Django Rest 框架中:

from rest_framework import serializers

class UpperCaseSerializerField(serializers.CharField):

    def __init__(self, *args, **kwargs):
        super(UpperCaseSerializerField, self).__init__(*args, **kwargs)

    def to_representation(self, value):
        value = super(UpperCaseSerializerField, self).to_representation(value)
        if value:
            return value.upper()

并在 serializers.py 中使用,例如:

class OrderCreateSerializer(serializers.ModelSerializer):
    
    #Force uppercase
    item = UpperCaseSerializerField()
    class Meta:
        model = Order
        fields = ['order_id', 'item' ]

这确实有效,因为输入被转换为大写,但是,模型约束不再被考虑

有效负载示例 1:

{ "the_item": "first" } - LEGIT, converted in FIRST

有效负载示例 2:

{ "the_item": "FIRST" } - LEGIT, it's already uppercase

有效负载示例 3:

{ "the_item": "1234567" } - COMPLETELY WRONG INPUT, but it is accepted! It should be not!

很明显它只关心大写,完全忽略了模型结构,不承认不同于'FIRST'和'SECOND'的字符串。

因此,删除 item = UpperCaseSerializerField()“恢复”了模型约束检查,但我失去了将所有字母大写的能力。

我错过了什么吗?有没有办法同时保留两者?

我认为明确声明序列化程序字段会阻止序列化程序重用该特定字段的模型验证逻辑,正如您已经注意到的那样,因为在某些情况下您可能真的想摆脱一些验证。

您可以通过为 ChoiceFields 创建另一个字段 class 来快速获得相同的验证,其中包含以下内容:

class UpperCaseSerializerChoiceField(serializers.ChoiceField):

    def to_representation(self, value):
        value = super().to_representation(value)
        if value:
            return value.upper()

和re-add序列化器的选择:

class OrderCreateSerializer(serializers.ModelSerializer):
    
    item = UpperCaseSerializerChoiceField(choices=Order.Item.choices)
    class Meta:
        model = Order
        fields = ['order_id', 'item' ]

请注意,我没有尝试代码

编辑: 根据 source code,方法 to_internal_value 负责验证选择(这实际上是有道理的,在呈现数据时调用 to_representation,即对于 API 响应).

所以使用类似下面的东西应该会有所帮助:

class UpperCaseSerializerChoiceField(serializers.ChoiceField):

    def to_internal_value(self, data):
        if data:  # In case the field is nullable
            data = data.upper()
        return super().to_internal_value(data)