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

问题描述 投票:0回答:1

我正在尝试将现有的或新的项目从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': 
[]}
python django-rest-framework patch serializer
1个回答
0
投票

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

所以我通过转义菜单项的验证为MenuItem创建了一个单独的序列化程序,并在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
© www.soinside.com 2019 - 2024. All rights reserved.