我正在try 为一个模型创建自定义验证,以判断它的start_date在它的end_date之前,事实证明它几乎是不可能的.

我试过的东西:

  • 内置Django验证器:不判断此

  • 写我自己的,就像这样:

    def validate_date(self):
       if self.start_date < self.end_date:
            raise serializers.ValidationError("End date must be after start date.")
    

我添加到Serializer类(然后是模型)中的那部分代码,但似乎在两个位置都没有被调用.

我还发现了可能有用的this位代码,但我不知道如何集成到我的方法中——似乎可以验证一个模型属性,但我需要在两个属性之间进行判断.

我的模型:

class MyModel(models.Model):

    created = models.DateTimeField(auto_now_add=True)
    relation_model = models.ForeignKey(RelationModel, related_name="mymodels")
    priority = models.IntegerField(
        validators = [validators.MinValueValidator(0), validators.MaxValueValidator(100)])
    start_date = models.DateField()
end_date = models.DateField()

    @property
    def is_active(self):
        today = datetime.date.today()
        return (today >= self.start_date) and (today <= self.end_date)

    def __unicode__(self):
        ...

    class Meta:
        unique_together = ('relation_model', 'priority', 'start_date', 'end_date')

仅供参考,所有其他验证都有效!

我的串行器:

class MyModelSerializer(serializers.ModelSerializer):

    relation_model = RelationModelSerializer
    is_active = serializers.Field(source='is_active')

    def validate_date(self):
        if self.start_date > self.end_date:
            raise serializers.ValidationError("End date must be after start date.")   

    class Meta:
        model = MyModel
        fields = (
            'id', 'relation_model', 'priority', 'start_date', 'end_date', 'is_active'
        )

我的观点是:

class MyModelList(generics.ListCreateAPIView):
    permission_classes = (IsAdminUser,)
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer
    ordering = ('priority')

推荐答案

您应该使用对象范围的验证(validate()),因为validate_date永远不会被调用,因为date不是序列化程序上的字段.From the documentation:

class MySerializer(serializers.ModelSerializer):
    def validate(self, data):
        """
        Check that the start is before the stop.
        """
        if data['start_date'] > data['end_date']:
            raise serializers.ValidationError("finish must occur after start")
        return data

按照Michel Sabchuk的建议,您可以将验证错误添加到end_date字段:

class MySerializer(serializers.ModelSerializer):
    def validate(self, data):
        """
        Check that the start is before the stop.
        """
        if data['start_date'] > data['end_date']:
            raise serializers.ValidationError({"end_date": "finish must occur after start"})
        return data

另一种可能性是创建验证器.我根据UniqueTogetherValidator的代码创建了一个:

from rest_framework.utils.representation import smart_repr

class DateBeforeValidator:
    """
    Validator for checking if a start date is before an end date field.
    Implementation based on `UniqueTogetherValidator` of Django Rest Framework.
    """
    message = _('{start_date_field} should be before {end_date_field}.')

    def __init__(self, start_date_field="start_date", end_date_field="end_date", message=None):
        self.start_date_field = start_date_field
        self.end_date_field = end_date_field
        self.message = message or self.message

    def __call__(self, attrs):
        if attrs[self.start_date_field] > attrs[self.end_date_field]:
            message = self.message.format(
                start_date_field=self.start_date_field,
                end_date_field=self.end_date_field,
            )
            # Replace the following line with
            #   raise serializers.ValidationError(
            #       {self.end_date_field: message},
            #       code='date_before',
            #   )
            # if you want to raise the error on the field level
            raise serializers.ValidationError(message, code='date_before')

    def __repr__(self):
        return '<%s(start_date_field=%s, end_date_field=%s)>' % (
            self.__class__.__name__,
            smart_repr(self.start_date_field),
            smart_repr(self.end_date_field)
        )


class MySerializer(serializers.ModelSerializer):
    class Meta:
        # If your start/end date fields have another name give them as kwargs tot the
        # validator:
        #   DateBeforeValidator(
        #       start_date_field="my_start_date", 
        #       end_date_field="my_end_date",
        #   )
        validators = [DateBeforeValidator()]

在DRF 3.0之前,您也可以将其添加到模型的clean函数中,但在DRF 3.0中不再调用它.

class MyModel(models.Model):
    start_date = models.DateField()
    end_date = models.DateField()
    def clean(self):
        if self.end_date < self.start_date:
            raise ValidationError("End date must be after start date.")

Django相关问答推荐

查尔而不是瓦尔查尔加盟Django 球场

Django BooleanField如何使用RadioSelect?

Django:无法分配必须是实例(&Q;X),不想获取对象(&Q;)

获取PyCharm中继承方法的未解析属性引用

错误404除主要应用程序外,HTML页面无法渲染的其他应用程序

一次请求中更新整个Django模型

AttributeError:模块rest_framework.serializers在 Swagger 中的 Django 中没有属性NullBooleanField

如果一个应用程序有多个具有相同字段的模型,那么保持 DRY 的最佳实践是什么?

Django:使用 Django ORM 实现 JOIN?

用于测试文件下载的 Django 单元测试

在 Django 中提供大文件(高负载)

如何在 Django 模板上实现 back链接?

使用 Gunicorn 运行 Django - 最佳实践

Django Admin:如何在同一视图中显示来自两个不同模型的字段?

如何使用 django-nose 运行单个测试或单个 TestCase?

Django - 了解 X-Sendfile

模拟 Django 查询集以测试采用查询集的函数

PyCharm 代码判断提示找不到模板文件,如何解决?

python/django中setattr和对象操作的区别

Django admin - 使所有字段只读