I have been able to replicate the create method to add the correct nested serializers in a POST request. However, I'm still having issues updating in a PUT or PATCH. When using a PUT or PATCH request and I pass the entire object data or the "brands" data, it will only update in the position it is passed. So if I have an object with 3 values:
"brands": [
{
"id": 1,
"name": "Brand 1 Test"
},
{
"id": 2,
"name": "Brand 2 Test"
},
{
"id": 3,
"name": "Brand 3 Test"
}
}
If I pass:
"brands": [
{
"id": 1,
"name": "Brand 1 Test"
},
{
"id": 2,
"name": "Brand 2 Test"
}
It will give me the same list of 3 brands. But if I do that in reverse order it will update and add the 3rd brand. I'm not sure what's causing it. Here's the code I have:
Models
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)
Serializers
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
I think the issue lies somewhere here. If any more code is needed please let me know(ex. views, urls). I'm guessing in the update I'm not properly emptying the list of brands. I just can't see it. Any help would be appreciated.
CodePudding user response:
I think the clue here is that you do instance.brands.add
, which does exactly that, adding. Not removing as you noticed :)
You also have a set
.
So:
brand_objs = []
for brand_data in brands:
brand = Brand.objects.get(**brand_data)
brand_objs.append(brand)
instance.brands.set(brand_objs)
But the usage could differ, I can imagine that you'd also want to be able to just add one, or more, brands? But could use different end points for that?
Endpoints example
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?