ManyToMany关系自定义序列化程序 [英] ManyToMany relation custom serializer
问题描述
class IPAddress(models.Model):
address = models.CharField ()
类规则(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 validated_data ['ips']]
但是在序列化器中找不到正确的方式来显示这个我已经写了一个这样的串行器:
class RuleSerializer(serializers.Serializer):
name = serializers.CharField()
ips = serializers.SlugRelatedField(many = True,slug_field ='address',validators = [],queryset = models.IPAddress.objects.all())
$ c但是问题是它在请求中验证了ip,如果没有这样的ip,它会返回错误,尽管我将验证器设置为空列表。
我有两个问题,如何禁用此验证?而我的方式是编写序列化器和模型是适合我的场景(我无法更改我得到的请求和我必须发送的响应)
解决方案如果您需要以以下格式返回 Rule
的实例:
{
name:Foo,
ips:[192.168.1.40,4.4.4.4,8.8.8.8]
}
您可以创建一个 RuleSerializer
并使用 SlugRelatedField
。
SlugRelatedField
仅适用于已存在的对象。既然您也将创建对象,您可以修改 to_internal_value
实现来创建不存在的对象(从这里引用):
class CreatableSlugRelatedField(serializers.SlugRelatedField):
def to_internal_value(self,data):
try:
return self.get_queryset()。get_or_create(** {self.slug_field:data})[0]
除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'#这是ip地址
queryset = IPAddress.objects.all()
)
class M eta:
model = Rule
fields:('name','ips')
更新:根据问题中的评论:
我无法更改我得到的请求以及必须发送的响应
但是,如果您可以使用嵌套的序列化程序代表需要稍微改变:
{
name:Foo,
ips :[
{地址:192.168.1.40},
{地址:4.4.4.4},
{地址:8.8.8.8}
]
}
然后嵌套的序列化程序(更多文档在这里):
class IPAddressSerializer(serializers.ModelSerializer):
class Meta:
model = IPAddress
字段:('address',)
class RuleSerializer(serializers.ModelSerializer):
ips = IPAddressSerializer(many = True)
class Meta:
model = Rule
fields:('name','ips')
Assume I have two models:
class IPAddress(models.Model):
address = models.CharField()
class Rule(models.Model):
name = models.CharField()
ips = models.ManyToMany(IPAddress)
I want to able to add a rule by a request as below:
{
"name":"Foo",
"ips":["192.168.1.40", "4.4.4.4", "8.8.8.8"]
}
Also I want to construct ip in each request (there is no url to construct an ip directly) for new rule, so I have written a class for manager like this:
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']]
But in serializer I could not find a proper way to show this I have written a serializer like this:
class RuleSerializer(serializers.Serializer):
name = serializers.CharField()
ips = serializers.SlugRelatedField(many=True, slug_field='address', validators=[], queryset=models.IPAddress.objects.all())
But the problem is it validates the ip in request and if there is not such ip it returns an error, although I set the validators an empty list.
I have two questions, how can I disable this validation? And is my way for writing the serializer and model is appropriate for my scenario(I could not change the request I get and the response I must send)
解决方案 If you need to return an instance of Rule
in following format:
{
"name":"Foo",
"ips":["192.168.1.40", "4.4.4.4", "8.8.8.8"]
}
You could create a RuleSerializer
and use SlugRelatedField
.
SlugRelatedField
only works with the objects which already exist. Since you would be creating objects as well, you could modify the to_internal_value
implementation to create the objects which doesn't exist (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')
UPDATE: Based on the comments in the question:
I could not change the request I get and the response I must send
But if you could then use nested serializers though your representation would need to change slightly:
{
"name": "Foo",
"ips": [
{"address": "192.168.1.40"},
{"address": "4.4.4.4"},
{"address": "8.8.8.8"}
]
}
and, then the nested serializers (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')
这篇关于ManyToMany关系自定义序列化程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!