1. ホーム
  2. django

[解決済み] Django REST フレームワークのカスタムフィールドの検証

2022-12-16 12:16:20

質問

あるモデルに対してカスタムバリデーションを作成しようとしています。 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クラス(そしてモデル)に追加しましたが、どちらの場所でも呼び出されることはないようです。

また、私は この のコードも見つけましたが、私のメソッドにどのように統合すればよいのかわかりません。

私のモデルです。

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 はシリアライザーのフィールドではないので、呼び出されることはありません。 ドキュメントから :

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()]

DRF3.0以前は、モデルのクリーン関数に追加することもできましたが、DRF3.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.")