Question

I have a queryset containing some objects. Depending on some case or the other i now want to exclude all the objects without certain tags (_tags is the name of the TagField on my model):

self.queryset=self.queryset.exclude(_tags__id__in=avoid)

But this just leaves me with an error:

Caught FieldError while rendering:
Join on field '_tags' not permitted.
Did you misspell 'id' for the lookup type?

As i'm pretty sure i did not misspell 'id', i did some searching on how to use tagging for something like this. In the docs there is a lot about custom Managers, but somehow i just can't get it how i can use them to get what i want.

edit:

corrected the code above to

self.queryset=self.queryset.exclude(_tags__in=avoid)

where avoid is a list of integers. And that leaves me with the problem that the TagField of django-tagging is just a special CharField (or TextField?). Which will, of course, not sort out anything if i just query it against a list of integers. I could try to solve this in a way like this:

for tag in avoid:
    self.queryset=self.queryset.exclude(_tags__contains=tag.name)

which is not only ugly, but also leaves me with the problem of tags made of multiple words or matching parts of other tags.

I somehow have the suspicion that this could be solved in a much prettier way by someone who has understood how django-tagging works.

Was it helpful?

Solution 3

As described in the comment on Chris' answer, django-tagging does not deliver the tagstring when accessing model._tag. In the end i had no other solution than to do the query and sort out the loops containing a certain tag afterwards:

itemlist = list(queryset)
avoid = some_list_of_tag_ids            
# search for loops that have NONE of the avoid tags
for item in itemlist:
    #    has tags   and [ if a tag.id in avoid this list has an element]
    if (item.tags)  and [tag for tag in item.tags if tag.id in avoid]:
        # remove the item from the list
        itemlist.remove(item)

To complete that the model for this looks like this:

class Item(models.Model):

    _tags = TagField(blank=True,null=True)

    def _get_tags(self):
        return Tag.objects.get_for_object(self)
    def _set_tags(self, tags):
        Tag.objects.update_tags(tags)

    tags = property(_get_tags, _set_tags)

Allthough i tried for quite a while, i found no way of chaining a query against tagging tags into a query chain for an object. For this project I'm stuck with tagging, but this is a real drawback...

OTHER TIPS

How are your models defined? Is _tags a ForeignKey field?

if not remove the __id part

self.queryset=self.queryset.exclude(_tags__in=avoid)

Unfortunately, no, there's no prettier way. In fact, the actual solution is even uglier, but when all the tags are stored in a single text field, there's no other way:

from django.db.models import Q

startswith_tag = Q(_tags__startswith=tag.name+' ')
contains_tag = Q(_tags__contains=' '+tag.name+' ')
endswith_tag = Q(_tags__endswith=' '+tag.name)
self.queryset=self.queryset.exclude(startswith_tag | contains_tag | endswith_tag)

The code above assumes that tags are delimited with spaces. If not, you'll have to modify the code to match how they are delimited. The idea is that you use the delimiter as part of the search to ensure that it's the actual tag and not just part of another tag.

If you don't want to do it this way, I'd suggest switching to another tag system that doesn't dump them all into a single text field, django-taggit for instance.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top