モデルのカスタム検証を作成して、その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 が役に立つかもしれないコードを見つけましたが、私のメソッドに統合する方法がわかりません-1つのモデル属性を検証するのにうまくいくようですが、 2つの属性間。
私のモデル:
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')
Fyi、他のすべての検証は機能します!
私のシリアライザー:
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')
date
はシリアライザのフィールドではないため、validate_date
は呼び出されないため、オブジェクト全体の検証(validate()
)を使用する必要があります。 ドキュメントから :
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
DRF 3.0より前では、モデルのクリーン関数に追加することもできましたが、これは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.")
jgadelangeの答えは、おそらくDjango rest 3より前に機能しました。 Djangoレストフレームワーク3 *バージョンを使用している人がいれば、これはその人にとって役立つと思います。検証プロセスをモデルレベルで維持する必要があり、クリーンなメソッドが1つのソリューションになる場合があります。しかし、Django残りのフレームワークの発表では here と言います。誰かがmodel .cleanメソッドで残りの呼び出しを検証したい場合、シリアライザのvalidateメソッドをオーバーライドし、次の方法でこのシリアライザクラスからメソッドを削除します
(docは言う:clean()メソッドはシリアライザー検証の一部として呼び出されないため)
class MySerializer(serializers.ModelSerializer):
def validate(self, attrs):
instance = MyModel(**attrs)
instance.clean()
return attrs
とモデル
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.")
ここでの別の答えは、シリアライザーのvalidate()
メソッドをオーバーライドすることを選択した場合の状況に関して役立つかもしれません。
Django REST Frameworkのシリアライザー検証の順序 に関する回答については、 serializer.validate()
メソッドが検証シーケンス。ただし、その前にフィールドのバリデーターが呼び出されます serializer.to_internal_value()
で、最後にValidationError
を上げます。
これは、カスタム検証エラーがデフォルトのエラーと重ならないことを意味します。
私の意見では、望ましい動作を実現する最もクリーンな方法は、シリアライザクラスで ターゲットフィールドメソッド 検証を使用することです。
def validate_end_date(self, value):
# validation process...
return value
この場合のstart_date
など、モデルから別のフィールド値が必要な場合は、次のようにして取得できます(プロセスは完了していないため、まだ検証されていません)。
# `None` here can be replaced with field's default value
start_date = 'start_date' in self.initial_data
and self.initial_data['start_date'] or None
これをフィールドでクラスベースのバリデーターとして実装するのに苦労している場合...
from rest_framework.serializers import ValidationError
class EndDateValidator:
def __init__(self, start_date_field):
self.start_date_field = start_date_field
def set_context(self, serializer_field):
self.serializer_field = serializer_field
def __call__(self, value):
end_date = value
serializer = self.serializer_field.parent
raw_start_date = serializer.initial_data[self.start_date_field]
try:
start_date = serializer.fields[self.start_date_field].run_validation(raw_start_date)
except ValidationError:
return # if start_date is incorrect we will omit validating range
if start_date and end_date and end_date < start_date:
raise ValidationError('{} cannot be less than {}'.format(self.serializer_field.field_name, self.start_date_field)
シリアライザーにstart_date
およびend_date
フィールドがあると仮定すると、validators=[EndDateValidator('start_date')]
でend_date
フィールドに設定できます。
Konradの回答を拡大します。非常に明示的であり、他のフィールドを使用するときに他のフィールドの検証を呼び出しているため、私はそれが好きです。したがって、より安全で、おそらく冗長になります(一部の検証は2回呼び出されます)
最初に注意することは、このように実装すると、run_validatorを実行したときに、validators変数に設定された検証のみが表示されることです。したがって、たとえばvalidate_メソッドを使用してフィールドを検証した場合、そのフィールドは実行されません。
また、私はそれを継承可能にしたので、検証関数を再実装してコードを再利用できます。
validators.py
from rest_framework.serializers import ValidationError
class OtherFieldValidator:
#### This part is the same for all validators ####
def __init__(self, other_field):
self.other_field = other_field # name of parameter
def set_context(self, serializer_field):
self.serializer_field = serializer_field # name of field where validator is defined
def make_validation(self,field, other_field):
pass
def __call__(self, value):
field = value
serializer = self.serializer_field.parent # serializer of model
raw_other_field = serializer.initial_data[self.other_field] # data del otro campo
try:
other_field = serializer.fields[self.other_field].run_validation(raw_other_field)
except ValidationError:
return # if date_start is incorrect we will omit validating range
#### Here is the only part that changes ####
self.make_validation(field,other_field)
class EndDateValidator(OtherFieldValidator):
def make_validation(self,field, other_field):
date_end = field
date_start = other_field
if date_start and date_end and date_end < date_start:
raise ValidationError('date cannot be')
したがって、シリアライザーは次のようになります。serializers.py
# Other imports
from .validators import EndDateValidator
def myfoo(value):
raise ValidationError("start date error")
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
extra_kwargs = {
'date_end': {'validators': [EndDateValidator('date_start')]},
'date_start': {'validators': [myfoo]},
}