我正在寻找的是包含未标记的任何对象的QuerySet。
到目前为止,我提出的解决方案看起来过于复杂了:
# Get all tags for model
tags = Location.tags.all().order_by('name')
# Get a list of tagged location id's
tag_list = tags.values_list('name', flat=True)
tag_names = ', '.join(tag_list)
tagged_locations = Location.tagged.with_any(tag_names) \
.values_list('id', flat=True)
untagged_locations = []
for location in Location.objects.all():
if location.id not in tagged_locations:
untagged_locations.append(location)
有任何改进的想法吗?谢谢!
答案 0 :(得分:3)
这篇文章中有一些很好的信息,所以我觉得不应该删除它,但是有一个更简单的解决方案
我快速浏览了django-tagging的源代码。看起来他们使用ContentType框架和通用关系来实现它。
因此,您应该能够在Location类上创建generic reverse relation,以便轻松访问给定位置的TaggedItem对象(如果您还没有这样做):
from django.contrib.contenttypes import generic
from tagging.models import TaggedItem
class Location(models.Model):
...
tagged_items = generic.GenericRelation(TaggedItem,
object_id_field="object_id",
content_type_field="content_type")
...
<强>澄清强>
我的原始回答建议这样做:
untagged_locs = Location.objects.filter(tagged_items__isnull=True)
虽然这可以用于'普通连接',但这实际上在这里不起作用,因为内容类型框架会在content_type_id
的{{1}}中对isnull
进行额外的检查:
SELECT [snip] FROM `sotest_location`
LEFT OUTER JOIN `tagging_taggeditem`
ON (`sotest_location`.`id` = `tagging_taggeditem`.`object_id`)
WHERE (`tagging_taggeditem`.`id` IS NULL
AND `tagging_taggeditem`.`content_type_id` = 4 )
你可以像这样反转它来解决它:
untagged_locs = Location.objects.exclude(tagged_items__isnull=False)
但那感觉不对。
我也提出过这个问题,但有人指出annotations don't work as expected有内容类型框架。
from django.db.models import Count
untagged_locs = Location.objects.annotate(
num_tags=Count('tagged_items')).filter(num_tags=0)
上述代码在我的有限测试用例中适用于我,但如果您的模型中有其他“可标记”对象,则可能会出错。原因是它没有检查the ticket中概述的content_type_id
。它生成了以下SQL:
SELECT [snip], COUNT(`tagging_taggeditem`.`id`) AS `num_tags`
FROM `sotest_location`
LEFT OUTER JOIN `tagging_taggeditem`
ON (`sotest_location`.`id` = `tagging_taggeditem`.`object_id`)
GROUP BY `sotest_location`.`id` HAVING COUNT(`tagging_taggeditem`.`id`) = 0
ORDER BY NULL
如果Location
是您唯一的可标记对象,那么上述方法就可以了。
建议的解决方法
没有让注释机制起作用,这就是我在此期间会做的事情:
untagged_locs_e = Location.objects.extra(
where=["""NOT EXISTS(SELECT 1 FROM tagging_taggeditem ti
INNER JOIN django_content_type ct ON ti.content_type_id = ct.id
WHERE ct.model = 'location'
AND ti.object_id = myapp_location.id)"""]
)
这为SQL添加了一个额外的WHERE子句:
SELECT [snip] FROM `myapp_location`
WHERE NOT EXISTS(SELECT 1 FROM tagging_taggeditem ti
INNER JOIN django_content_type ct ON ti.content_type_id = ct.id
WHERE ct.model = 'location'
AND ti.object_id = myapp_location.id)
它加入django_content_type
表以确保您正在查看相应的表
在您有多个可标记模型类型的情况下,模型的内容类型。
更改myapp_location.id
以匹配您的表名。可能有一种方法可以避免对表名进行硬编码,但是如果它对你很重要,你可以弄明白。
如果您不使用MySQL,请相应地进行调整。
答案 1 :(得分:0)
试试这个:
[location for location in Location.objects.all() if location.tags.count() == 0]
答案 2 :(得分:0)
假设您的Location
班级使用tagging.fields.TagField
实用程序。
from tagging.fields import TagField
class Location(models.Model):
tags = TagField()
你可以这样做:
Location.objects.filter(tags='')