修改
为了防止每个人阅读太多:问题是我不明白是什么
Q(tag_rep__tag=tag.id)
确实适用于此型号:
class Item(models.Model):
tag_rep=generic.GenericRelation(TaggedItem)
很可能tag_rep__tag会返回标记列表,而不是单个标记。所以我不能像我做的那样做查询。我如何比较tag_rep__tag以确定它是否
我正在尝试为具有标记标记的项目构建基于查询集的tagfilter。我已经成功完成了过滤器的创建,但它们基于pythons filter
并返回一个列表:
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)
这很好用,但现在我需要一个queryset来传递给一个formset。因此,当前方法不再起作用,因为它返回一个列表。为了能够访问我模型的标签,我做了这个:
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_tags
和seek_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
应排除具有avoid_tags中任何标记的每个项目。最后一个seek_all_tags
应该只返回包含all_tags中所有标记的项目。
但是avoid_any_tags
无效。它只排除了在avoid_tags中没有标签的元素。如果某个项目的任何标记不在avoid_tags中,则不会将其排除。为什么不呢?
答案 0 :(得分:1)
我认为这不是一个完全合适的查询(来自您的示例):
Q(tag_rep__tag=tag.id)
我假设您正在接近examples listed here,并且该TaggedItem的标记字段是一个slug(字符串)字段?所以马上过滤,过滤id等于slugfield似乎不对。此外,您的GenericRelation表示列表(或该问题的查询集)是正确的。它是一个反向引用,返回TaggedItem,指定其foreignkey字段。所以你可以这样做: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)
答案 1 :(得分: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)
但也许这是一个自己的问题。