如何通过补丁请求将其他型号的现有项目添加为M2M字段



我正在尝试将MenuItem模型中的现有项或新项添加到使用ManyToMany字段链接的ItemCategory模型中。若数据库中不存在MenuItem对象,它运行良好,但无法添加现有的MenuItem,它表示类似于menu_item的东西已经存在。

查看

class ItemCategoryView(generics.RetrieveUpdateDestroyAPIView):
"""Update, Edit or delete ItemCategory objects"""
serializer_class = ItemCategorySerializer
queryset = ItemCategory.objects.all()
lookup_field = 'id'
def get(self, request, id=None):
return self.retrieve(request)
def put(self, request, id=None):
return self.update(request, id)
def delete(self, request, id):
return self.destroy(request, id)

序列化程序

class ItemCategorySerializer(serializers.ModelSerializer):
"""Serializer for ItemCategory objects"""
item = MenuItemSerializer(many=True, required=False)
class Meta:
model = ItemCategory
fields = ('id', 'name', 'item')
read_only_fields = ('id', )
# Creating with nested serializer
def create(self, validated_data):
menu_item = validated_data.pop('item', None)
category = ItemCategory.objects.create(**validated_data)
# if list of menu item info is passed
if menu_item:
items = []
for item in menu_item:
itm = MenuItem.objects.create(**item)
items.append(itm)
category.item.add(*items)
return category
def update(self, instance, validated_data):
"""Works perfectly in case if item doesn't exists"""
new_items = validated_data.pop('item')
items_to_be_added_to_category = []
for new_item in new_items:
try:
item_obj = MenuItem.objects.get(**new_item)
except MenuItem.DoesNotExist:
item_obj = MenuItem.objects.create(**new_item)
items_to_be_added_to_category.append(item_obj)
instance.item.add(*items_to_be_added_to_category)
instance.save()
return instance

失败的测试用例

def test_add_existing_item_to_category(self):
"""Test adding existing item object to category"""
category_obj = sample_category("Mutton Special")
item_obj, item_obj_info = sample_item(
item_name="Mutton Sekuwa",
payload_only=False
)
payload = {
"name": category_obj.name,
"item": [item_obj_info]
}
res = self.client.patch(
reverse(UPDATE_ITEM_CATEGORY_URL, kwargs={'id': category_obj.id}),
payload,
format="json"
)
category_obj = ItemCategory.objects.get(id=category_obj.id)
category_item = category_obj.item.all()
serializer = ItemCategorySerializer(category_obj)

self.assertEqual(res.data, serializer.data)
self.assertIn(item_obj, category_item)
self.assertEqual(res.status_code, status.HTTP_200_OK)

错误:

======================================================================
FAIL: test_add_existing_item_to_category (Menu.tests.test_category_api.ItemCategoryAPITest)
Test adding existing item object to category
----------------------------------------------------------------------
Traceback (most recent call last):
File "/rms/Menu/tests/test_category_api.py", line 117, in test_add_existing_item_to_category
self.assertEqual(res.data, serializer.data)
AssertionError: {'item': [{'item_name': [ErrorDetail(string=[62 chars])]}]} != {'id': 2, 'name': 'Mutton Special', 'item': 
[]}

实际上,问题是ItemCategorySerializer中的项目验证不允许添加现有项目。

因此,我通过转义MenuItem的验证为其创建了一个seprate序列化程序,并在MenuItemSerializer上引入了一个名为action的新字段,该字段可用于从同一api端点删除/删除特定的模型对象。

class NestedMenuItemSerializer(serializers.ModelSerializer):
"""Serializer for updating item objects in ItemCategory"""
ACTION_CHOICE = (
('remove', 'remove'),
('delete', 'delete')
)
action = serializers.ChoiceField(
choices=ACTION_CHOICE,
required=False
)
class Meta:
model = MenuItem
fields = (
'id',
'item_name',
'price',
'description',
'item_type',
'image',
'action'
)
extra_kwargs = {
'item_name': {'validators': []}, # escaping validation
'action': {'validators': []}
}
read_only_fields = ('id', 'url')
class UpdateItemCategorySerializer(serializers.ModelSerializer):
"""Serializer for Creating ItemCategory objects"""
item = NestedMenuItemSerializer(many=True, read_only=False)
class Meta:
model = ItemCategory
fields = ('id', 'name', 'item')
read_only_fields = ('id', )
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
items_info = validated_data.pop('item')
for item_info in items_info:
action = item_info.pop('action', None)
if action:
item_obj = get_object_or_404(MenuItem, **item_info)
if action is "remove":
instance.item.remove(item_obj)
elif action is "delete":
item_obj.delete()
else:
item_name = item_info.pop('item_name', None)
item_obj, _ = MenuItem.objects.get_or_create(
item_name=item_name,
defaults=item_info
)
instance.item.add(item_obj)
instance.save()
return instance

最新更新