edit:
To prevent everyone from reading too much: The problem is that i do not understand what
Q(tag_rep__tag=tag.id)
really does for this model:
class Item(models.Model):
tag_rep=generic.GenericRelation(TaggedItem)
Most probably tag_rep__tag does return a list of tags, not a single tag. So i can’t just do the query like i did it. How can i compare tag_rep__tag to find out if it
- contains any of a list of tags
- contains all of a list of tags
I’m trying to build a queryset based tagfilter for items that have tagging tags. I allready succeeded with the creation of the filters, but they are based on pythons filter and return a list:
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)
This worked pretty well, but now i need a queryset in return to pass into a formset. So the current method is not working anymore, as it returns a list. To be able to access the tags of my model i did this:
from django.contrib.contenttypes import generic
from tagging.models import TaggedItem
class Item(models.Model):
# some_fields here
tag_rep = generic.GenericRelation(TaggedItem)
The filter seek_any_tags was the only one i could allready rebuild in form of a database query. After reading some SO posts and doing a lot of googling i came up with this solution:
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
Quite nice, and yes, i’m a bit proud about it. But now there are two more filters i want to build: avoid_any_tags and seek_all_tags. I tried to do them in the same way i did the seek_any_tags filter:
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 should exclude every item that has any of the tags in avoid_tags. The last one, seek_all_tags, should only return items that have all of the tags in all_tags.
But avoid_any_tags doesn’t work as exected. It only excludes elements that have tags in avoid_tags and none else. If an item has any tag that is not in avoid_tags it is not excluded. Why not?
I don’t think this is a fully appropriate query (from your examples):
I am assuming you are following close to the examples listed here, and that the tag field of that TaggedItem is a slug (string) field? So right off the bat, filtering where the id is equal to the slugfield does not seem right. Also, you are correct that your GenericRelation represents a list (or a queryset for that matter). Its a reverse reference back to the TaggedItem specifying its foreignkey field. So you would be able to do:
Item.tag_rep.all()There is so much extra information in your question that its a bit hard to follow, so I am just going to ignore most of that and address your summary at the top.
As for the “has all tags in given tag list”, maybe there is a really complex django query based way to do this, but here is an approach with the intermediate steps being on the client side:
In this last example, it would match situations where an Item has tags
['a,'b']and the all_tags is['a','b',c'], but it would not match situations where the Item has more tags than are in all_tags, but the ones it has do match. To have it match either way, you would have to modify that item_ids filter: