我要找的是一个包含所有未加标签的对象的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)有什么改进的想法吗?谢谢!
发布于 2009-12-01 03:16:17
在这篇文章中有一些很好的信息,所以我不认为应该删除它,但有一个简单得多的解决方案
我快速浏览了一下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)虽然这可以用于“普通连接”,但实际上在这里不起作用,因为内容类型框架在isnull的SQL中抛出了对content_type_id的额外检查:
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)上面的代码适用于我有限的测试用例,但是如果你的模型中有其他“可加标签”的对象,它可能会有but。原因是它不会检查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,请进行相应的调整。
发布于 2009-12-01 03:10:20
试试这个:
[location for location in Location.objects.all() if location.tags.count() == 0]发布于 2009-12-02 00:50:20
假设您的Location类使用tagging.fields.TagField实用程序。
from tagging.fields import TagField
class Location(models.Model):
tags = TagField()你可以这样做:
Location.objects.filter(tags='')https://stackoverflow.com/questions/1821658
复制相似问题