Django-DRF验证

时间:2018-07-03 10:42:54

标签: python django django-rest-framework django-validation

我正在设计一个API,它将由不同的合作伙伴使用。以下是API虚拟有效负载:-

{
    key1: value1,
    key2: value2,
    key3: value3,
    key4: value4,
    key5: value5,
    partner: partner_code
}

现在,我有一个模型,其中需要保存以上字段。

class Table(models.Model):
    key1 = models.IntegerField()    
    key2 = models.IntegerField(blank=True, null=True)    
    key3 = models.IntegerField()
    key4 = models.IntegerField(blank=True, null=True)    
    key5 = models.CharField(max_length=255)
    partner = models.ForeignKey(Partner)

另外,我有一个序列化器:-

class TableSerializer(models.Model):
    class Meta:
        model = Table
        fields = '__all__'

现在,每当发布数据时,我都会序列化(并验证)数据,然后将其保存。这是我的方法。

serializer = TableSerializer(data=payload)
if serializer.is_valid():
    serializer.save()

这是正常的DRF流程。现在,出现的问题是我需要对每个合作伙伴应用自定义验证。例如:-

`key2` and `key4` are mandatory for PartnerA. Similarly, for PartnerB, max value of `key1` is 100 and many more.

根据当前的DRF流,我需要在序列化器中添加if-else条件。

class TableSerializer(models.Model):
    def validate(self, data):
        if partner == `partnerA`:
            # checkfor key1 max value.
            # check the mandatory fields.
        elif partner == `partnerB`:
            # some custom validations
        # and so on
    class Meta:
        model = Table
        fields = '__all__'

这个if-else可能会继续增长,这是一个糟糕的设计。如何引入另一个名为 Validation Engine 的组件,它可以首先验证数据,然后将经过验证的数据传递给DRF序列化器?

2 个答案:

答案 0 :(得分:0)

您可以使用partner == partnerB或类似的方法编写更多的序列化器(该序列化器仅用于验证),并执行以下工作:

def validate(self, data):
    data_is_valid = False
    for serializer_class in selializers_for_validate:
        serializer = serializer_class(data=data)
        is_valid = serializer.is_valid()
        if is_valid:
            break
    if not is_valid:
        raise serializers.ValidationError(<here your custom error>)
    return data

我认为这个决定不是很好,但是您可以尝试, 或者您可以编写自定义验证器并在validate中使用

答案 1 :(得分:0)

我建议仅通过基本验证以及合作伙伴id / name / type / etc与特定序列化器之间的映射来制作基本序列化器:

from django.db import models
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet


# models.py
class Table(models.Model):
    key1 = models.IntegerField()
    key2 = models.IntegerField(blank=True, null=True)
    key3 = models.IntegerField()
    key4 = models.IntegerField(blank=True, null=True)
    key5 = models.CharField(max_length=255)
    partner = models.ForeignKey(Partner)


# serializers.py
class TableSerializer(serializers.ModelSerializer):
    partner_serializers = {}

    class Meta:
        model = Table
        fields = '__all__'

    def validate(self, data):
        self.partner_serializers[data['partner']['partner_id']](
            instance=self.instance,
            data=data
        ).is_valid(raise_exception=True)

        return super().validate(data)

    @classmethod
    def register_partner_validator(cls, partner_id):
        def wrapped(serializer):
            cls.partner_serializers[partner_id] = serializer
            return serializer

        return wrapped


@TableSerializer.register_partner_validator('thePartner1')
class Partner1TableSerializer(TableSerializer):
    class Meta:
        extra_kwargs = {
            'key3': {'max_value': 3}
        }

    def validate(self, data):
        if data['key1'] + data['key2'] > 5:
            raise serializers.ValidationError('Wrong!')


@TableSerializer.register_partner_validator('thePartner2')
class Partner2TableSerializer(TableSerializer):
    def validate(self, data):
        if data['key1'] + data['key5'] < 5:
            raise serializers.ValidationError('Wrong!')


# views.py
class TableViewSet(ModelViewSet):
    queryset = Table.objects.all()
    serializer_class = TableSerializer