Django Rest Framework序列化器和Django表单

10

问题: 如何编写DRY代码,以在表单和序列化器中进行字段验证?

示例: 我有一个简单的Django应用程序,其中模型表单验证Orderpassengers字段:

def clean_passengers(self):
    passengers = self.cleaned_data['passengers']
    if passengers > self.flight.available_seats:
        raise forms.ValidationError(
            _(u'''Passengers count can`t be greater then seats count'''))
    return passengers

Order 序列化器中,用同样的代码进行验证:

def validate_passengers(self, attrs, source):
    passengers = attrs[source]
    if passengers > self.flight.available_seats:
        raise serializers.ValidationError(
            _(u'''Passengers count can`t be greater then seats count'''))
    return attrs

这不是DRY的,我需要写两次相同的逻辑。我该如何避免这种情况?也许我可以从表单中继承序列化器或类似的东西。

3个回答

8
您可以在表单的is_valid方法中使用序列化器对数据进行反序列化和验证。
class MyModelForm(ModelForm):
    def is_valid(self):
        # Call super's is_valid to populate cleaned_data and do basic field validation
        valid = super(MyModelForm, self).is_valid()
        if not valid:
            return False

        serializer = MyModelSerializer(data=self.cleaned_data)
        return serializer.is_valid()

1
这个很好用,谢谢。但是表单错误为空。要添加它们需要添加:for error, value in serializer.errors.iteritems(): self._errors[error] = value - zymud
与其使用私有属性_errors,你可以使用add_error(field, error) 方法。 - nnutter

5

这里是我为模型表单实现的可重用验证类。除了重新编写的get_serializer方法外,所有原生的Django内容都在这里。已通过Django 1.8.13的验证工作状态。

class RestFrameworkValidationModelForm(forms.ModelForm):
    serializer_class = None

    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer()` method."
            % self.__class__.__name__
        )

        return self.serializer_class(*args, **kwargs)

    def is_valid(self):
        if super(RestFrameworkValidationModelForm, self).is_valid():
            serializer = self.get_serializer(data=self.cleaned_data)
            valid = serializer.is_valid()
            self.add_error(None, serializer.errors)
            return valid
        return False

以下是使用示例:

class ExperimentForm(RestFrameworkValidationModelForm):
    serializer_class = ExperimentSerializer

    class Meta:
        model = Experiment
        exclude = []

2

我建议把所有的验证(如果可能的话)放到模型中(验证器或clean)。

ModelFormModelSerializer然后使用模型验证。


1
谢谢您的回答,但问题是关于表单和序列化器的一般性问题。我使用了模型表单和模型序列化器作为例子。 - zymud
据我所知,当使用Model/clean方法时,您无法在字段和对象级别执行单步验证。 - jturmel
我也喜欢在模型中尽可能多地进行验证,因为管理员和序列化器可以很好地处理它。但是一旦需要请求用户,情况就会变得棘手。 - djangonaut
1
很遗憾,他们不再调用“Model”的“clean”方法了:https://github.com/encode/django-rest-framework/issues/3144 - MrObjectOriented
这个答案已经过时了,因为DRF不再使用Model.clean()并完全跳过该验证。链接 - Brad Solomon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接