删除DRF中与PrimaryKeyRelatedField的ManyToMany关系

时间:2018-07-21 11:42:48

标签: django django-rest-framework

我有两个这样的模型:

Tags(models.Model):
    key = models.CharField(unique=True, max_length=50)
    value = models.TextField()


Note(models.Model):
    key = models.CharField(unique=True, max_length=50)
    tags = models.ManyToManyField(Tag)

标记在这里用于对Notes进行分类,就像此处的StackOverflow问题一样。当客户端创建/更新Notes时,我希望它们能够发送Tag ID列表以与其关联。这很好。当他们获取注释时,我希望标签完整,而不仅仅是ID列表。因此,我将PrimaryKeyRelatedField子类化并覆盖to_representation

class RelatedTagsField(PrimaryKeyRelatedField):
    def to_representation(self, value):
        return {
            'id': value.id,
            'key': value.key,
            'value': value.value
        }


class NoteSerializer(ModelSerializer):
    tags = RelatedTagsField(queryset=Tag.objects.all(),
                            many=True,
                            required=False,
                            allow_empty=True)

    class Meta:
        model = Note
        fields = '__all__'

当创建Notes并通过发送包含列表中标签ID的PATCH添加标签时,此方法效果很好,但是在发送空列表以取消注释标签时,此方法不起作用。 DRF根本不执行任何操作(QueryDict为空,validated_data也为空)。

这里是一个单元测试来说明问题:

class TaggedNoteTestCase(APITestCase):
    # setUpTestData omitted
    def test_update(self):
        # create a tag
        response = self.client.post(
            f'/api/tags/',
            {
                'key': 'mytag',
                'value': 'bla'
            }
        )
        self.assertEqual(response.status_code, 201)
        tag_id = response.data['id']
        response = self.client.get('/api/tags/')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.data), num_tags + 1)

        # create a note with the tag
        response = self.client.post(
            '/api/notes/',
            {
                'key': 'testkey',
                'value': 'blabla',
                'tags': [tag_id]
            })
        self.assertEqual(response.status_code, 201)
        self.assertGreater(len(response.data), 0)
        note_id = response.data['id']

        # check tag
        response = self.client.get(f'/api/notes/{note_id}/')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['tags'][0]['id'], tag_id)

        # create another tag
        response = self.client.post(
            f'/api/tags/',
            {
                'key': 'mytag2',
                'value': 'bla'
            }
        )
        self.assertEqual(response.status_code, 201)
        self.assertGreater(len(response.data), 0)
        tag2_id = response.data['id']

        # update note to add 2nd tag
        response = self.client.patch(
            f'/api/notes/{note_id}/',
            {
                'tags': [tag_id, tag2_id]
            }
        )
        self.assertEqual(response.status_code, 200)
        self.assertGreater(len(response.data), 0)
        self.assertEqual(len(response.data['tags']), 2)

        # remove tags
        response = self.client.patch(
            f'/api/notes/{note_id}/',
            data={
                'tags': []
            }
        )
        self.assertEqual(response.status_code, 200)
        self.assertGreater(len(response.data), 0)
        self.assertEqual(len(response.data['tags']), 0)
        # ^^^^^^^^^ this fails, number of tags is still 2!

我希望DRF使用ID列表来设置与注释相关的标签。这在使用POST创建带有标签的注释时有效,在通过发送带有PATCH的ID列表添加或删除现有注释的标签关联时有效。但是,当我想通过发送带有PATCH的空标签ID列表来删除所有标签关联时,此方法不起作用。

我在做什么错?为什么我可以使用ID列表来设置ManyToMany关系来添加和更改关系,但不能通过传递空列表来完全删除它们?

1 个答案:

答案 0 :(得分:0)

解决方案是在API测试客户端中设置format='json'。感谢HåkenLid解决了这个问题。