代码之家  ›  专栏  ›  技术社区  ›  lemonad

使用django标记检索未标记的对象

  •  1
  • lemonad  · 技术社区  · 15 年前

    我要找的是一个包含任何未标记对象的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)
    

    有改进的想法吗?谢谢

    3 回复  |  直到 15 年前
        1
  •  3
  •   Joe Holloway    15 年前

    这篇文章中有一些很好的信息,所以我不认为应该删除它,但有一个非常简单的解决方案

    我快速浏览了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,请进行相应的调整。

        2
  •  0
  •   jcdyer Anand S Kumar    15 年前

    试试这个:

    [location for location in Location.objects.all() if location.tags.count() == 0]
    
        3
  •  0
  •   Joe Holloway    15 年前

    Location 类使用 tagging.fields.TagField 效用。

    from tagging.fields import TagField
    class Location(models.Model):
        tags = TagField()
    

    Location.objects.filter(tags='')
    
    推荐文章