ManyToMany关系自定义序列化程序

时间:2016-04-18 06:11:50

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

假设我有两个模型:

class IPAddress(models.Model):
    address = models.CharField()

class Rule(models.Model):
    name = models.CharField()
    ips = models.ManyToMany(IPAddress)

我希望能够通过请求添加规则,如下所示:

{
"name":"Foo",
"ips":["192.168.1.40", "4.4.4.4", "8.8.8.8"]
}

另外我想在每个请求中构建ip(没有url直接构造一个ip)用于新规则,所以我为这样的经理写了一个类:

class RuleManager(models.Manager):
    def create(self, validated_data):
        rule = Rule(name=validate_data['name'])
        rule.save()

        rule.ips = [IPAddress.objects.get_or_create(item.lower()) for item in validated_data['ips']]

但是在序列化器中我找不到一个正确的方法来显示这个我编写了这样的序列化器:

class RuleSerializer(serializers.Serializer):
    name = serializers.CharField()
    ips = serializers.SlugRelatedField(many=True, slug_field='address', validators=[], queryset=models.IPAddress.objects.all())

但问题是它验证请求中的ip并且如果没有这样的ip它会返回错误,尽管我将验证器设置为空列表。

我有两个问题,如何禁用此验证?我的编写序列化程序和模型的方式适用于我的场景(我无法更改我得到的请求和我必须发送的响应)

1 个答案:

答案 0 :(得分:2)

如果您需要以下列格式返回Rule的实例:

{
"name":"Foo",
"ips":["192.168.1.40", "4.4.4.4", "8.8.8.8"]
}

您可以创建RuleSerializer并使用SlugRelatedField

SlugRelatedField仅适用于已存在的对象。由于您也将创建对象,因此您可以修改to_internal_value实现以创建不存在的对象(referenced from here):

class CreatableSlugRelatedField(serializers.SlugRelatedField):

    def to_internal_value(self, data):
        try:
            return self.get_queryset().get_or_create(**{self.slug_field: data})[0]
        except ObjectDoesNotExist:
            self.fail('does_not_exist', slug_name=self.slug_field, value=smart_text(data))
        except (TypeError, ValueError):
            self.fail('invalid')

class RuleSerializer(serializers.ModelSerializer):

    ips = serializers.CreatableSlugRelatedField(
        many=True,
        slug_field='address' # this is the ip address
        queryset=IPAddress.objects.all()
    )

    class Meta:
         model = Rule
         fields: ('name', 'ips')

更新:根据问题中的评论:

  

我无法更改我收到的请求和我必须发送的回复

但是如果你可以使用嵌套的序列化程序,虽然你的表示需要稍微改变一下:

{
    "name": "Foo",
    "ips": [
         {"address": "192.168.1.40"}, 
         {"address": "4.4.4.4"}, 
         {"address": "8.8.8.8"}
    ]
}

然后是嵌套的序列化器(more documentation here):

class IPAddressSerializer(serializers.ModelSerializer):

     class Meta:
         model = IPAddress
         fields: ('address',)

class RuleSerializer(serializers.ModelSerializer):

    ips = IPAddressSerializer(many=True)

    class Meta:
         model = Rule
         fields: ('name', 'ips')