如何正确更新多对多嵌套序列化程序?
How to properly update a many to many nested serializer?
我已经能够复制创建方法以在 POST 请求中添加正确的嵌套序列化程序。但是,我在 PUT 或 PATCH 中更新时仍然遇到问题。当使用 PUT 或 PATCH 请求并传递整个对象数据或“品牌”数据时,它只会更新传递的位置。因此,如果我有一个具有 3 个值的对象:
"brands": [
{
"id": 1,
"name": "Brand 1 Test"
},
{
"id": 2,
"name": "Brand 2 Test"
},
{
"id": 3,
"name": "Brand 3 Test"
}
}
如果我通过:
"brands": [
{
"id": 1,
"name": "Brand 1 Test"
},
{
"id": 2,
"name": "Brand 2 Test"
}
它将给我相同的 3 个品牌列表。但如果我以相反的顺序执行此操作,它将更新并添加第三个品牌。我不确定是什么原因造成的。这是我的代码:
车型
class Brand(models.Model):
name = models.CharField(max_length=500)
class Incentive(models.Model):
name = models.CharField(max_length=500)
brands = models.ManyToManyField(Brand, related_name='incentives_brand')
start_dt = models.DateTimeField(auto_now_add=False, blank=True, null=True)
end_dt = models.DateTimeField(auto_now_add=False, blank=True, null=True)
序列化程序
class BrandSerializer(serializers.ModelSerializer):
class Meta:
model = Brand
depth = 1
fields = ['id', 'name']
class IncentiveSerializer(serializers.ModelSerializer):
brands = BrandSerializer(many=True)
class Meta:
model = Incentive
fields = ['id', 'name', 'brands', 'start_dt', 'end_dt']
def create(self, validated_data):
brands = validated_data.pop('brands', [])
instance = Incentive.objects.create(**validated_data)
for brand_data in brands:
brand = Brand.objects.get(**brand_data)
instance.brands.add(brand)
return instance
def update(self, instance, validated_data):
brands = validated_data.pop('brands', [])
instance = super().update(instance, validated_data)
for brand_data in brands:
brand = Brand.objects.get(**brand_data)
instance.brands.add(brand)
return instance
我认为问题就出在这里。如果需要更多代码,请告诉我(例如视图、网址)。我猜在更新中我没有正确清空品牌列表。我只是看不到它。任何帮助将不胜感激。
我认为这里的线索是您 instance.brands.add
确实如此,添加。没有像您注意到的那样删除:)
你还有一个set
.
所以:
brand_objs = []
for brand_data in brands:
brand = Brand.objects.get(**brand_data)
brand_objs.append(brand)
instance.brands.set(brand_objs)
但用法可能会有所不同,我可以想象您也希望能够只添加一个或多个品牌?但是可以为此使用不同的终点吗?
端点示例
api/incentive/1/brands # get
api/incentive/1/brands # post, set brands?
api/incentive/1/brands/add # add one or more?
api/incentive/1/brands/remove # remove specific one or more?
我已经能够复制创建方法以在 POST 请求中添加正确的嵌套序列化程序。但是,我在 PUT 或 PATCH 中更新时仍然遇到问题。当使用 PUT 或 PATCH 请求并传递整个对象数据或“品牌”数据时,它只会更新传递的位置。因此,如果我有一个具有 3 个值的对象:
"brands": [
{
"id": 1,
"name": "Brand 1 Test"
},
{
"id": 2,
"name": "Brand 2 Test"
},
{
"id": 3,
"name": "Brand 3 Test"
}
}
如果我通过:
"brands": [
{
"id": 1,
"name": "Brand 1 Test"
},
{
"id": 2,
"name": "Brand 2 Test"
}
它将给我相同的 3 个品牌列表。但如果我以相反的顺序执行此操作,它将更新并添加第三个品牌。我不确定是什么原因造成的。这是我的代码:
车型
class Brand(models.Model):
name = models.CharField(max_length=500)
class Incentive(models.Model):
name = models.CharField(max_length=500)
brands = models.ManyToManyField(Brand, related_name='incentives_brand')
start_dt = models.DateTimeField(auto_now_add=False, blank=True, null=True)
end_dt = models.DateTimeField(auto_now_add=False, blank=True, null=True)
序列化程序
class BrandSerializer(serializers.ModelSerializer):
class Meta:
model = Brand
depth = 1
fields = ['id', 'name']
class IncentiveSerializer(serializers.ModelSerializer):
brands = BrandSerializer(many=True)
class Meta:
model = Incentive
fields = ['id', 'name', 'brands', 'start_dt', 'end_dt']
def create(self, validated_data):
brands = validated_data.pop('brands', [])
instance = Incentive.objects.create(**validated_data)
for brand_data in brands:
brand = Brand.objects.get(**brand_data)
instance.brands.add(brand)
return instance
def update(self, instance, validated_data):
brands = validated_data.pop('brands', [])
instance = super().update(instance, validated_data)
for brand_data in brands:
brand = Brand.objects.get(**brand_data)
instance.brands.add(brand)
return instance
我认为问题就出在这里。如果需要更多代码,请告诉我(例如视图、网址)。我猜在更新中我没有正确清空品牌列表。我只是看不到它。任何帮助将不胜感激。
我认为这里的线索是您 instance.brands.add
确实如此,添加。没有像您注意到的那样删除:)
你还有一个set
.
所以:
brand_objs = []
for brand_data in brands:
brand = Brand.objects.get(**brand_data)
brand_objs.append(brand)
instance.brands.set(brand_objs)
但用法可能会有所不同,我可以想象您也希望能够只添加一个或多个品牌?但是可以为此使用不同的终点吗?
端点示例
api/incentive/1/brands # get
api/incentive/1/brands # post, set brands?
api/incentive/1/brands/add # add one or more?
api/incentive/1/brands/remove # remove specific one or more?