Как правильно обновить вложенный сериализатор "многие ко многим"?
Мне удалось воспроизвести метод create, чтобы добавить правильные вложенные сериализаторы в 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 брендов. Но если я сделаю это в обратном порядке, он обновится и добавит 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)
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
Я думаю, что проблема кроется где-то здесь. Если потребуется дополнительный код, пожалуйста, дайте мне знать (например, представления, урлы). Я предполагаю, что в обновлении я не правильно очищаю список брендов. Я просто не могу его увидеть. Любая помощь будет оценена по достоинству.
Я думаю, что разгадка здесь в том, что вы делаете 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?
Добавьте instance.brands.clear() следующим образом:
Это очистит связанные бренды, чтобы вы могли обновить их заново.
def update(self, instance, validated_data):
brands = validated_data.pop('brands', None)
instance = super().update(instance, validated_data)
# The condition below will update brands only if brands were
# specified in the request body
if brands is not None:
instance.brands.clear() # Clear related brands
for brand_data in brands:
brand = Brand.objects.get(**brand_data)
instance.brands.add(brand)
return instance