【问题标题】:Common custom validation for DRF and Django adminDRF 和 Django 管理员的通用自定义验证
【发布时间】:2017-01-09 06:23:33
【问题描述】:

我有一个用户表,其中必须对员工用户设置唯一的电子邮件限制,对非员工用户设置唯一的电话限制。现在我想在 DRF API 和 Django 管理员中维护这个约束,如何在不重复代码的情况下实现这一点。

正如我从 DRF 3.0 开始看到的那样,所有验证都是为序列化程序显式执行的(如果我覆盖了 unique_validation 或 clean 方法来执行条件验证,我也必须为序列化程序单独编写这个自定义验证)所以如何编写适用于 DRF API 和 Django 管理员的自定义验证,无需重复代码?

【问题讨论】:

  • 在模型级别添加验证器
  • @utkbansal 但根据 DRF 文档,模型级别验证将不适用于 DRF 序列化程序 django-rest-framework.org/api-guide/validators“使用 REST 框架,验证完全在序列化程序类上执行。”
  • @r.bhardwaj 您应该将约束添加到模型级别。然后在序列化程序和管理表单或管理类本身中尝试/捕获异常。无法为 admin 和 APIView 创建通用验证
  • @IvanSemochkin 明白了。但是这里有一个更好的解决方案stackoverflow.com/questions/32921956/…你怎么看?

标签: django django-rest-framework


【解决方案1】:

如果您的模型没有嵌套关系,这应该可以工作。


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

但就我而言,我有嵌套模型,我需要覆盖序列化程序中的 create() 和 update() 方法。我将验证保存在一个地方的解决方案:


def create(self, validated_data):
        # Remove nested and M2m relationships from validated_data
        firstmodel_set = validated_data.pop('firstmodel_set') if 'firstmodel_set' in validated_data else []
        .....
        # Get instance of project
        instance = Project(**validated_data)
        instance.clean()

        # If clean method doesn't raise any exception, create.
        project = instance.save()
        ......

如果你想序列化验证消息..也许这样的东西会帮助你......

def validate_instance(instance):
    try:
        # Call model validation
        instance.clean()
    except ValidationError as e:
        raise serializers.ValidationError(e)

或者您可以使用 Signals 并在 pre_save() 上进行验证。 看看https://docs.djangoproject.com/en/1.10/topics/signals/

【讨论】:

  • 我在validate_instance 中看到你捕获了ValidationError,然后将其提升为serializers.ValidationError。这两个异常真的如此不同以至于您需要 try/except 块吗?如果没有,将instance.clean() 作为整个函数体就足够了。
  • @MattMcCarthy 不幸的是,它们的不同之处足以让您的前端注意到。目前我喜欢这种方法:github.com/HackSoftware/…,包含且明确。或者你可以在 drf 异常处理程序中做同样的事情。
【解决方案2】:

对于 API: 为 UserRegistration 创建一个序列化程序,并使用两个条件实现验证方法,一个用于唯一电子邮件,另一个用于电话号码。 类似于:

class UserCreateSerializer(ModelSerializer):
   class Meta:
        model = User
        fields = ['email','phone','...']
   def validate(self, data):
        """user email phone validator """

        user_qs = User.objects.filter(email=data['email'], is_staff=True)
        if user_qs.exists():
            raise ValidationError({"email":"This user has already registered."})
        user_phone = User.objects.filter(phone=data['phone'], is_active=True, is_staff=False)
        if user_phone.exists():
            raise ValidationError({"phone":"Phone number already exists."})
        return data

网页版: 如果您已经创建了注册表单,那么在您的表单中实现 clean 方法作为序列化程序中的验证,上面。

class UserCreateForm(ModelForm):

    class Meta:
            model = User
            fields = ['email','phone','...']
    def clean(self):
            """user email phone validator """

            user_qs = User.objects.filter(email=self.cleaned_data['email'], is_staff=True)
            if user_qs.exists():
                raise ValidationError({"email":"This user has already registered."})
            user_phone = User.objects.filter(phone=self.cleaned_data['phone'], is_active=True, is_staff=False)
            if user_phone.exists():
                raise ValidationError({"phone":"Phone number already exists."})
            return data

【讨论】:

  • 好吧,也就是说我们得分别定义DRF Serializer和Django Admin表单的验证,没有通用的方法吧?
  • 如果我需要同时验证两个字段,例如:将名称与 id 匹配在一起,我需要做什么?
  • 我找到了解决方案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-08
相关资源
最近更新 更多