Проверка пользовательских полей Django REST Framework
Я пытаюсь создать пользовательскую проверку для модели, чтобы проверить, что ее 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')
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')
Ответы
Ответ 1
Вы должны использовать широкую проверку объекта (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
Pre 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.")
Ответ 2
Ответ jgadelange работал до того, как django rest 3 вероятно. Если кто-то использует версию django rest framework 3 *, я думаю, что это было бы полезно для этого народа. следует продолжать процесс проверки на уровне модели, и единственным методом может быть метод очистки. Но объявление django rest framework говорит здесь, что если кто-то хочет проверить метод rest-call в методе modelclean, он/она должен переопределить метод проверки serializer и должен вызывать чистый метод для этого класса сериализатора следующим образом
(потому что 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.")
Ответ 3
Другой ответ здесь может быть полезен, если вы решите переопределить метод serialify validate()
.
Что касается ответа на порядок проверки Serializer в Django REST Framework, я должен сказать, что метод serializer.validate()
вызывается в конце последовательности проверки. Тем не менее, валидаторы полей вызываются до этого, в serializer.to_internal_value()
, поднимая ValidationError
в конце.
Это означает, что пользовательские ошибки проверки не складываются по умолчанию.
На мой взгляд, самым чистым способом достижения желаемого поведения является использование проверки метода целевого поля в классе serializer:
def validate_end_date(self, value):
# validation process...
return value
В случае, если вам понадобится другое значение поля из модели, например start_date
, вы можете получить их (но не подтвержденный, поскольку процесс не завершен):
# 'None' here can be replaced with field default value
start_date = 'start_date' in self.initial_data
and self.initial_data['start_date'] or None
Ответ 4
В случае, если кто-то борется с реализацией этого как основанного на классах валидатора в поле...
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
, вы можете установить в поле end_date
с помощью validators=[EndDateValidator('start_date')]
.