如何处理嵌套序列化程序的 PATCH 请求?
How to handle a PATCH request for a nested Serializer?
我正在尝试使用 Django Rest Framework 为 http://127.0.0.1:8000/api/v1/controller/device/<pk>/
上的设备详细信息端点构建端点
型号:-
设备型号
class AbstractDevice(OrgMixin, BaseModel):
name = models.CharField()
mac_address = models.CharField()
key = KeyField()
model = models.CharField()
os = models.CharField()
system = models.CharField()
notes = models.TextField(blank=True, help_text=_('internal notes'))
last_ip = models.GenericIPAddressField()
management_ip = models.GenericIPAddressField()
hardware_id = models.CharField()
配置模型
class AbstractConfig(BaseConfig):
device = models.OneToOneField(Device, on_delete=models.CASCADE)
templates = SortedManyToManyField()
vpn = models.ManyToManyField()
STATUS = Choices('modified', 'applied', 'error')
status = StatusField()
context = JSONField()
对于上述模型,我将序列化程序创建为:-
DeviceConfigSerializer
class DeviceConfigSerializer(serializers.ModelSerializer):
config = serializers.JSONField()
context = serializers.JSONField()
class Meta:
model = Config
fields = ['backend', 'status', 'templates', 'context', 'config']
DevicedetailSerializer
class DeviceDetailSerializer(serializers.ModelSerializer):
config = DeviceConfigSerializer()
class Meta(BaseMeta):
model = Device
fields = [
'id',
'name',
'organization',
'mac_address',
'key',
'last_ip',
'management_ip',
'model',
'os',
'system',
'notes',
'config',
]
def update(self, instance, validated_data):
instance = self.instance or self.Meta.model(**validated_data)
instance.name = validated_data['name']
instance.organization = validated_data['organization']
instance.mac_address = validated_data['mac_address']
instance.key = validated_data['key']
instance.last_ip = validated_data['last_ip']
instance.management_ip = validated_data['management_ip']
instance.model = validated_data['model']
instance.os = validated_data['os']
instance.system = validated_data['system']
instance.notes = validated_data['notes']
instance.config.backend = validated_data['config']['backend']
instance.config.status = validated_data['config']['status']
config_templates = validated_data['config']['templates']
instance.config.templates.clear()
for template in config_templates:
instance.config.templates.add(template.pk)
instance.config.context = json.loads(
json.dumps(validated_data['config']['context']),
object_pairs_hook=collections.OrderedDict,
)
instance.config.config = json.loads(
json.dumps(validated_data['config']['config']),
object_pairs_hook=collections.OrderedDict,
)
instance.save()
instance.config.save()
return instance
由于我想合并一个嵌套的序列化器并使其可写,所以需要手动添加.update
方法。
和观点:
观看次数
class DeviceDetailView(RetrieveUpdateDestroyAPIView):
serializer_class = DeviceDetailSerializer
queryset = Device.objects.all()
以上代码对于 PUT
请求工作正常,但是当我尝试发送补丁请求时,它需要所有字段,即,直到我输入所有字段,我无法发送请求,但是当我必须提供所有字段以更改单个字段时,这不是补丁请求。
ps:我已经对这道题的模型表示进行了抽象,并尝试给出模型的概念。
您可以使用 partial=True
覆盖补丁方法
class DeviceDetailView(RetrieveUpdateDestroyAPIView):
serializer_class = DeviceDetailSerializer
queryset = Device.objects.all()
def patch(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
或者你可以试试这个方法,为此你必须手动编写补丁
https://www.django-rest-framework.org/api-guide/serializers/#partial-updates
# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)
你可以通过partial=True
参考:https://www.django-rest-framework.org/api-guide/serializers/#partial-updates
partial
将在这里工作。所以就像
s = DeviceDetailSerializer(instance, data={'name': 'Manish'}, partial=True)
s.is_valid()
s.save()
如果您使用 patch
请求点击 URL,它将自动传递 partial
参数。
我在您的代码中发现的唯一问题是
- 你为什么要做
self.Meta.model(**validated_data)
?因为在更新方法中 self.instance
总是存在
- 当您执行
instance.organization = validated_data['organization']
时,您并没有检查 key
是否实际存在于 validated_data
中。所以你需要先检查密钥是否存在,如果密钥存在才更新数据。为此,您可以执行 if 'organization' in validated_data
然后执行 instance.organization = validated_data['organization']
其他一切看起来都不错,如果您仍然遇到任何问题,请更新该问题的描述以便更好地理解。
- 对于您的第一个疑问,补丁请求需要所有字段:
当您写 instance.organization = validated_data['organization']
时,您并没有检查 key
是否确实存在。
所以,为此你可以像 validated_data.get('organization')
那样做。这表示如果您的组织出现在经过验证的数据中,那么您的变量将被分配。
您正在将所有经过验证的数据提取到变量中,然后尝试更新它,但您可以使用此代码轻松完成。
instance = super().update(instance, validated_data)
return instance
这只会更新仅存在于 validated_data 中的您的字段。它不会更新其他字段。
- 对于第二个疑问,您还想更新嵌套的序列化程序字段
您可以直接从 validated_data 获取数据并更新它。
在这种情况下,您所做的是正确的,我认为它会完美无缺。
如果您仍然遇到任何问题,请在本帖中回复我,我会尽力为您解答。
我正在尝试使用 Django Rest Framework 为 http://127.0.0.1:8000/api/v1/controller/device/<pk>/
型号:-
设备型号
class AbstractDevice(OrgMixin, BaseModel):
name = models.CharField()
mac_address = models.CharField()
key = KeyField()
model = models.CharField()
os = models.CharField()
system = models.CharField()
notes = models.TextField(blank=True, help_text=_('internal notes'))
last_ip = models.GenericIPAddressField()
management_ip = models.GenericIPAddressField()
hardware_id = models.CharField()
配置模型
class AbstractConfig(BaseConfig):
device = models.OneToOneField(Device, on_delete=models.CASCADE)
templates = SortedManyToManyField()
vpn = models.ManyToManyField()
STATUS = Choices('modified', 'applied', 'error')
status = StatusField()
context = JSONField()
对于上述模型,我将序列化程序创建为:-
DeviceConfigSerializer
class DeviceConfigSerializer(serializers.ModelSerializer):
config = serializers.JSONField()
context = serializers.JSONField()
class Meta:
model = Config
fields = ['backend', 'status', 'templates', 'context', 'config']
DevicedetailSerializer
class DeviceDetailSerializer(serializers.ModelSerializer):
config = DeviceConfigSerializer()
class Meta(BaseMeta):
model = Device
fields = [
'id',
'name',
'organization',
'mac_address',
'key',
'last_ip',
'management_ip',
'model',
'os',
'system',
'notes',
'config',
]
def update(self, instance, validated_data):
instance = self.instance or self.Meta.model(**validated_data)
instance.name = validated_data['name']
instance.organization = validated_data['organization']
instance.mac_address = validated_data['mac_address']
instance.key = validated_data['key']
instance.last_ip = validated_data['last_ip']
instance.management_ip = validated_data['management_ip']
instance.model = validated_data['model']
instance.os = validated_data['os']
instance.system = validated_data['system']
instance.notes = validated_data['notes']
instance.config.backend = validated_data['config']['backend']
instance.config.status = validated_data['config']['status']
config_templates = validated_data['config']['templates']
instance.config.templates.clear()
for template in config_templates:
instance.config.templates.add(template.pk)
instance.config.context = json.loads(
json.dumps(validated_data['config']['context']),
object_pairs_hook=collections.OrderedDict,
)
instance.config.config = json.loads(
json.dumps(validated_data['config']['config']),
object_pairs_hook=collections.OrderedDict,
)
instance.save()
instance.config.save()
return instance
由于我想合并一个嵌套的序列化器并使其可写,所以需要手动添加.update
方法。
和观点:
观看次数
class DeviceDetailView(RetrieveUpdateDestroyAPIView):
serializer_class = DeviceDetailSerializer
queryset = Device.objects.all()
以上代码对于 PUT
请求工作正常,但是当我尝试发送补丁请求时,它需要所有字段,即,直到我输入所有字段,我无法发送请求,但是当我必须提供所有字段以更改单个字段时,这不是补丁请求。
ps:我已经对这道题的模型表示进行了抽象,并尝试给出模型的概念。
您可以使用 partial=True
覆盖补丁方法class DeviceDetailView(RetrieveUpdateDestroyAPIView):
serializer_class = DeviceDetailSerializer
queryset = Device.objects.all()
def patch(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
或者你可以试试这个方法,为此你必须手动编写补丁 https://www.django-rest-framework.org/api-guide/serializers/#partial-updates
# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)
你可以通过partial=True
参考:https://www.django-rest-framework.org/api-guide/serializers/#partial-updates
partial
将在这里工作。所以就像
s = DeviceDetailSerializer(instance, data={'name': 'Manish'}, partial=True)
s.is_valid()
s.save()
如果您使用 patch
请求点击 URL,它将自动传递 partial
参数。
我在您的代码中发现的唯一问题是
- 你为什么要做
self.Meta.model(**validated_data)
?因为在更新方法中self.instance
总是存在 - 当您执行
instance.organization = validated_data['organization']
时,您并没有检查key
是否实际存在于validated_data
中。所以你需要先检查密钥是否存在,如果密钥存在才更新数据。为此,您可以执行if 'organization' in validated_data
然后执行instance.organization = validated_data['organization']
其他一切看起来都不错,如果您仍然遇到任何问题,请更新该问题的描述以便更好地理解。
- 对于您的第一个疑问,补丁请求需要所有字段:
当您写 instance.organization = validated_data['organization']
时,您并没有检查 key
是否确实存在。
所以,为此你可以像 validated_data.get('organization')
那样做。这表示如果您的组织出现在经过验证的数据中,那么您的变量将被分配。
您正在将所有经过验证的数据提取到变量中,然后尝试更新它,但您可以使用此代码轻松完成。
instance = super().update(instance, validated_data)
return instance
这只会更新仅存在于 validated_data 中的您的字段。它不会更新其他字段。
- 对于第二个疑问,您还想更新嵌套的序列化程序字段
您可以直接从 validated_data 获取数据并更新它。
在这种情况下,您所做的是正确的,我认为它会完美无缺。
如果您仍然遇到任何问题,请在本帖中回复我,我会尽力为您解答。