0

编辑:

防止大家阅读过多:问题是我不明白什么

Q(tag_rep__tag=tag.id)

确实适用于这个模型:

class Item(models.Model):

    tag_rep=generic.GenericRelation(TaggedItem)

很可能 tag_rep__tag 确实返回标签列表,而不是单个标签。所以我不能像我那样做查询。我如何比较 tag_rep__tag 以确定它是否

  1. 包含任何标签列表
  2. 包含所有标签列表

我正在尝试为具有标记标签的项目构建一个基于查询集的标签过滤器。我已经成功创建了过滤器,但它们基于 pythonfilter并返回一个列表:

class TagFilter(object):

    def __init__(self,any_tags,avoid_tags,all_tags):
         # all tags as tag instances
         self.any_tags = any_tags
         self.avoid_tags = avoid_tags
         self.all_tags = all_tags

    def seek_any_tags(self, item):
        for tag in self.any_tags:
            if tag in item.tags:
               return True
        return False

    def items_with_any_tags(self,items):
        return filter(self.seek_any_tags,items)

这工作得很好,但现在我需要一个查询集作为回报来传递给一个表单集。所以当前方法不再起作用,因为它返回一个列表。为了能够访问我的模型的标签,我这样做了:

from django.contrib.contenttypes import generic
from tagging.models import TaggedItem

class Item(models.Model):

    # some_fields here

    tag_rep = generic.GenericRelation(TaggedItem) 

过滤器seek_any_tags是我唯一可以以数据库查询的形式重建的过滤器。在阅读了一些 SO 帖子并进行了大量谷歌搜索后,我想出了这个解决方案:

def seek_any_tags_qs(self,items):

    if self.any_tags!=None:
        q = reduce(lambda x, y: x | y, [Q(tag_rep__tag=tag.id) for tag in self.any_tags])
        items = items.filter(q)

    return items

非常好,是的,我对此感到有点自豪。但是现在我要构建另外两个过滤器:avoid_any_tagsseek_all_tags. 我尝试以与seek_any_tags过滤器相同的方式进行操作:

def avoid_any_tags_qs(self,items):

    if self.avoid_tags != None:
        q = reduce(lambda x, y: x | y, [Q(tag_rep__tag=tag.id) for tag in self.avoid_tags])
        items = items.exclude(q)

    return items


def seek_all_tags_qs(self,items):

    if self.all_tags != None:
        q = reduce(lambda x, y: x & y, [Q(_tags__contains=tag.name) for tag in self.seek_tags])
        items.filter(q)

    return items

avoid_any_tags应该排除在避免标签中具有任何标签的每个项目。最后一个,seek_all_tags应该只返回在 all_tags 中包含所有标签的项目。

avoid_any_tags不能像执行的那样工作。它只排除在 Avoid_tags 中有标签的元素,没有其他标签。如果一个项目有任何不在 Avoid_tags 中的标签,它不会被排除。为什么不?

4

2 回答 2

1

我认为这不是一个完全合适的查询(来自您的示例):

Q(tag_rep__tag=tag.id)

我假设您正在关注此处列出的示例,并且该 TaggedItem 的标记字段是 slug(字符串)字段?因此,立即过滤 id 等于 slugfield 的位置似乎不正确。此外,您的 GenericRelation 代表一个列表(或就此而言的查询集)是正确的。它是对指定其外键字段的 TaggedItem 的反向引用。所以你可以这样做:Item.tag_rep.all()

您的问题中有太多额外的信息,有点难以理解,所以我将忽略其中的大部分内容,并在顶部处理您的摘要。

# Any of a list of strings*
any_tags = ['foo', 'bar']
Item.objects.filter(tag_rep__tag__in=any_tags)

至于“在给定的标签列表中有所有标签”,也许有一个非常复杂的基于 django 查询的方法来做到这一点,但这是一种中间步骤在客户端的方法:

# items having all tags in a given list of tags
from collections import defaultdict

tag_vals = Item.objects.all().values_list('id', 'tags__tag').distinct()
# produces result like:  `[(1, u'bdfl'), (1, u'boom'), (2, u'bar'), ...)`

all_tags = set(['boom', 'fizz'])

tag_groups = defaultdict(set)
for id_, tag in tag_vals:
    tag_groups[id_].add(tag)
# produces tag_groups like `{1: set([u'fizz', u'foo', u'boom']), ...}`

item_ids = [id_ for id_,tags in d.iteritems() if tags.issubset(all_tags)]
Item.objects.filter(id__in=item_ids)

在最后一个示例中,它将匹配 Item 具有标签['a,'b']且 all_tags 为 的情况['a','b',c'],但它不会匹配 Item 具有的标签多于 all_tags 的情况,但它确实匹配的情况。要使其匹配任何一种方式,您都必须修改 item_ids 过滤器:

item_ids = [id_ for id_,tags in d.iteritems() if \
                tags.issubset(all_tags) or tags.issuperset(all_tags)]
Item.objects.filter(id__in=item_ids)
于 2012-05-25T17:57:15.490 回答
0

简而言之,这是有效的代码:

# does the item have any of the seek tags?
has_seek_tags = Q(tags__id__in=self.seek_tags)
items = items.filter(has_seek_tags).distinct()

# does the item have any of the avoid tags?    
has_avoid_tags= Q(tags__id__in=self.avoid_tags)
items = items.exclude(has_avoid_tags).distinct()

# now apply the queries
for tag_id in self.all_tags:
    items = items.filter(has_avoid_tags)

我仍然不明白的是,为什么 for 循环中的 items.filter 与此不同:

q = reduce(lambda x,y: x&y, [Q(tags__id__exact=tag_id) for tag_id in self.all_tags)
items = items.filter(q)

但也许这本身就是一个问题。

于 2012-05-25T20:27:58.303 回答