我正在创建一个django应用程序,它使用Django Rest Framework和普通的django-views作为用户的入口点。
我想验证模型中的独立字段以及整体上的对象。例如:
字段:输入的牌照是基于正则表达式函数的正确牌照。与其他领域无关。
对象:输入的邮政编码是否对指定国家/地区有效。与模型中的邮政编码和国家/地区相关。
对于DRF-API,我使用ModelSerializers自动调用我在模型中放置的所有验证器,例如:
class MyModel(models.Model):
licence_plate = CharField(max_length=20, validators=[LicencePlateValidator])
由于模型中给出了验证器,因此验证了API POSTS(因为我使用的是ModelSerializer)以及在django admin后端中创建的对象。
但是当我想引入对象级别验证时,我需要在序列化程序的 validate() - 方法中执行此操作,这意味着对象仅在API中进行验证。
我也必须覆盖模型的save方法,以验证在Django管理页面中创建的对象。
问题:这对我来说似乎有些混乱,有一点我可以放置对象级验证器,以便它们在API和管理页面中运行,就像我做了字段级验证(我只需要将他们放在我的模型声明中,一切都得到处理)
答案 0 :(得分:6)
对于模型级验证,有Model.clean
方法。
如果您使用ModelForm
(admin
中默认使用),则会调用它,因此这将解决django视图和管理部分。
另一方面,DRF不会打电话给模特' clean
会自动Serializer.validate
,因此您必须自己在class ValidateModelMixin(object)
def validate(self, attrs):
attrs = super().validate(attrs)
obj = self.Meta.model(**attrs)
obj.clean()
return attrs
class SomeModelSerializer(ValidateModelMixin, serializers.ModelSerializer):
#...
class Meta:
model = SomeModel
中自行完成(正如doc建议的那样)。你可以通过串行器mixin来实现:
class DelegateToModelValidator(object):
def set_context(self, serializer):
self.model = serializer.Meta.model
def __call__(self, attrs):
obj = self.model(**attrs)
obj.clean()
class SomeModelSerializer(serializers.ModelSerializer):
#...
class Meta:
model = SomeModel
validators = (
DelegateToModelValidator(),
)
或写一个验证器:
clean
警告:
答案 1 :(得分:2)
您可以创建一个单独的函数validate_zipcode_with_country(zipcode, country)
,这将需要2个参数zipcode
和country
。
然后,我们将在序列化程序的validate()
和我们的模型clean()
中调用此方法。
from django.core.exceptions import ValidationError
def validate_zipcode_with_country(zipcode, country):
# check zipcode is valid for the given country
if not valid_zipcode:
raise ValidationError("Zipcode is not valid for this country.")
然后在serializers.py
中,您需要在validate()
函数中调用此函数。
class MySerializer(serializers.ModelSerializer):
def validate(self, attrs):
zipcode = attrs.get('zipcode')
country = attrs.get('country')
validate_zipcode_with_country(zipcode, country) # call the function
...
同样,您需要覆盖模型clean()
并调用此函数。
class MyModel(models.Model):
def clean(self):
validate_zipcode_with_country(self.zipcode, self.country) # call this function
...