我有两个这样的模型:
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)
标签用于分类笔记,就像这里的StackOverflow问题一样。当客户端创建/更新笔记时,我希望他们能够发送一个标签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__'
这在创建笔记和通过发送一个包含列表中的标签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关系,以添加和更改关系,但不能通过传递一个空列表来完全删除它们?
2条答案
按热度按时间zzzyeukh1#
您的问题解决了,但是我想补充一下,当通过python请求发送请求时,要解决一个类似的问题
如果以这种方式发送请求并且遇到了此问题,请将data参数替换为json参数
bqf10yzr2#
解决方案是在API测试客户端设置
format='json'
,感谢Håken Lid解决了这个问题。