我有两个这样的模型:
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关系来添加和更改关系,但不能通过传递空列表来完全删除它们?
答案 0 :(得分:0)
解决方案是在API测试客户端中设置format='json'
。感谢HåkenLid解决了这个问题。