这篇文章中有一些很好的信息,所以我不认为应该删除它,但有一个非常简单的解决方案
我快速浏览了django标记的源代码。看起来他们使用了ContentType框架和泛型关系来实现它。
因此,您应该能够创建
generic reverse relation
在Location类上轻松访问给定位置的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
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)
上面的代码在我有限的测试用例中对我有效,但是如果您的模型中有其他“可标记”对象,那么可能会有问题。原因是它没有检查
内容类型id
the ticket
. 它生成了以下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)"""]
)
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,请进行相应的调整。