Django Rest Framework序列化器报错:传入含数组类型属性的JSON添加模型时出现异常
解决Django Rest Framework中IP数组序列化报错的问题
我一眼就看出问题所在了——你的序列化器期望接收的是嵌套对象数组,但你传入的是纯字符串IP数组,格式不匹配导致了报错。咱们一步步来解决:
问题根源
你的ComputerSerializer里的ip_address字段用了ComputerIPAddressSerializer(many=True),这意味着DRF会期望传入这样的结构:
{ "hostname":"PC-01", "os_system":"Windows 10", "ip_address": [ {"ip_address": "192.168.1.10"}, {"ip_address": "192.168.2.10"} ] }
但你实际传入的是纯字符串数组['192.168.1.10','192.168.2.10'],序列化器无法解析这种格式,自然就报错了。
解决方案:适配输入输出格式
我们需要让序列化器既能接收你给出的字符串IP数组,又能正确关联到ComputerIPAddress模型。下面是修改后的完整代码:
1. 先修改模型,添加反向关联名称
给ComputerIPAddress的外键加上related_name,让反向查询更直观:
class Computer(models.Model): hostname = models.CharField(max_length=32) os_system = models.CharField(max_length=60) class ComputerIPAddress(models.Model): # 添加related_name,方便通过Computer实例访问关联的IP computer = models.ForeignKey(Computer,on_delete=models.CASCADE, related_name='ip_addresses') ip_address = models.GenericIPAddressField(protocol='ipv4')
2. 重写序列化器,适配输入输出
我们用两个字段分别处理输入(字符串数组)和输出(嵌套IP对象),同时重写create和update方法来处理IP的关联逻辑:
class ComputerIPAddressSerializer(serializers.ModelSerializer): class Meta: model = ComputerIPAddress fields = ('id', 'ip_address') class ComputerSerializer(serializers.ModelSerializer): # 处理输入:只用于接收字符串IP数组,验证格式是否为合法IPv4 ip_address = serializers.ListField( child=serializers.IPAddressField(protocol='ipv4'), write_only=True ) # 处理输出:返回关联的IP对象数组,只读 ip_addresses = ComputerIPAddressSerializer(many=True, read_only=True) class Meta: model = Computer fields = ('id', 'hostname', 'os_system', 'ip_address', 'ip_addresses') def create(self, validated_data): # 取出IP数组,剩下的字段用于创建Computer实例 ips = validated_data.pop('ip_address') computer = Computer.objects.create(**validated_data) # 批量创建关联的IP地址,比循环创建更高效 ComputerIPAddress.objects.bulk_create([ ComputerIPAddress(computer=computer, ip_address=ip) for ip in ips ]) return computer def update(self, instance, validated_data): # 取出可能存在的IP数组(更新时可选) ips = validated_data.pop('ip_address', None) # 更新Computer的基础字段 instance.hostname = validated_data.get('hostname', instance.hostname) instance.os_system = validated_data.get('os_system', instance.os_system) instance.save() # 如果传入了新的IP数组,先删除旧的,再创建新的 if ips is not None: instance.ip_addresses.all().delete() ComputerIPAddress.objects.bulk_create([ ComputerIPAddress(computer=instance, ip_address=ip) for ip in ips ]) return instance
3. 视图集无需修改
你的ComputerViewSet可以保持原样,现在它就能正确处理你传入的JSON格式了:
{"hostname":"PC-01","os_system":"Windows 10","ip_address":["192.168.1.10","192.168.2.10"]}
额外说明
write_only=True确保ip_address字段只用于接收输入,不会在返回结果中出现;read_only=True则相反,ip_addresses只用于输出。IPAddressField(protocol='ipv4')会自动校验输入的IP是否为合法的IPv4地址,避免无效数据存入数据库。- 用
bulk_create批量创建IP对象,比循环调用create性能更好,尤其是当IP数量较多时。
内容的提问来源于stack exchange,提问作者Guille
相关产品推荐
相关产品推荐

