介绍
django-taggit是一个通用的,易用的标签系统,有了它以后,我们可以轻松地给任何模型打标签,并能很方便的对标签进行管理。
安装:
pip install django-taggit
使用方法
- 注册django-taggit应用
INSTALLED_APPS = [
...
'taggit',
...
]
- 在model中添加TaggableManager字段
from django.db import models
from taggit.managers import TaggableManager
class Food(models.Model):
# ... fields here
tags = TaggableManager()
- 后台添加标签以及查询方法
>>> apple = Food.objects.create(name="apple")
# 添加标签
>>> apple.tags.add("red", "green", "delicious")
# 查看单签对象标签
>>> apple.tags.all()
[<Tag: red>, <Tag: green>, <Tag: delicious>]
# 删除对象的某个标签
>>> apple.tags.remove("green")
>>> apple.tags.all()
[<Tag: red>, <Tag: delicious>]
# 设置标签,已有标签会被清理掉。
>>> apple.tags.set('red','big')
# 查询包含某一标签的食物
>>> Food.objects.filter(tags__name__in=["red"])
[<Food: apple>, <Food: cherry>]
- 当主键为UUID时,如何处理?
# 定义UUID的TaggedItem,用于保存
class UUIDTaggedItem(GenericTaggedItemBase, TaggedItemBase):
class Meta:
verbose_name = "UUID标签项"
verbose_name_plural = verbose_name
index_together = [["content_type", "object_id"]]
unique_together = [["content_type", "object_id", "tag"]]
# 实例化TaggbleManager是通过through参数传入UUIDTaggedItem ,替换原来的TaggedItem
tags = TaggableManager(blank=True,through=UUIDTaggedItem)
DRF中的使用
如何搜索
解决思路:在查询方法中,添加tags字段的查询处理逻辑,实现查询。
生产环境中,除了当前模型有标签外,可能在某些外键中也包含标签。那么考虑重写ModelViewSet的get_search_factor()方法[注:该方法来自自定义的GenericAPIView],添加对tags字段的支持,代码如下:
from django.db.models import Q
tag_fields = [] # 存放包含tags的列,可能是tags也可能是外键。
def get_search_factor(self,search_filter, contain_filter=True, regex=True):
...
if 'tags' in search_filter:
search_filter_tags = self.get_tags_search_filter(search_filter['tags'])
del search_filter['tags']
...
def get_tags_search_filter(self, tag_str):
"""
获取tags字段的查询条件,可能有多个tags列,由tag_fields决定。
:params tag_str: 可以有多个tag,用英文逗号(,)隔开
:return:
"""
search_filter_tags = None
tag_list = tag_str.split(',')
for tag_field in self.tag_fields:
if not search_filter_tags :
search_filter_tags = Q()
search_filter_tags.connector = 'OR'
if tag_field == 'tags':
search_filter_tags.children.append(('tags__name__in', tag_list))
else:
search_filter_tags.children.append(('%s__tags__name__in' % tag_field, tag_list))
return search_filter_tags
如何展示
# 自定义TagSerializerField,将多个tag用英文逗号隔开。
class TagSerializerField(serializers.Field):
def __init__(self, **kwargs):
# 默认只读
kwargs['read_only'] = True
super().__init__(**kwargs)
def to_representation(self, data):
return ','.join(data.values_list('name', flat=True))
def to_internal_value(self, data):
return None
# 序列化器使用该字段。
class TagSerializer(serializers.ModelSerializer):
tags = TagSerializerField()
如何通过API保存标签
# 请求时,多个标签用英文逗号(,)隔开
{
...
"tags":"中心合同,2020"
...
}
# 在perform_create处理新增, perform_update处理更新。
def perform_create(self, serializer):
instance = serializer.save()
if 'tags' in self.request.DATA:
instance.tags.set(*self.request.DATA['tags'].split(','))