假设我有两个模型:
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,它会返回一个错误,尽管我为验证器设置了一个空列表。
我有两个问题,如何禁用此验证?我编写序列化程序和模型的方式是否适合我的场景(我无法更改我得到的请求和必须发送的响应)
如果您需要以以下格式返回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]
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"}
]
}
然后是嵌套的序列化程序(此处有更多文档):
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')