How do I search for objects without specific tags?

I have a request that contains some objects. Depending on one case or the other, I now want to exclude all objects without specific tags (_tags is the name of the TagField in my model):

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

      

But that only 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 sure I didn't miss the 'id', I did some searches on how to use tags for something like this. The docs have a lot about custom managers, but somehow I just can't get it, how can I use them to get what I want.

change

adjusted the above code 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 django-tagging TagField is just a special CharField (or TextField?). Which of course won't make sense if I just ask it against a list of integers. I could try to solve this problem 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 a problem with multi-word tags or overlapping parts of other tags.

I somehow have a suspicion that this could be solved much more beautifully for those who understood how django tagging works.

+3


source to share


3 answers


As described in a comment on Chris's answer, django-tagging does not deliver a tagstring when accessed model._tag

. In the end, I had no other solution but to execute the query and sort the loops containing a specific tag, after that:

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 the model, it 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)

      

Everything I've tried for a while didn't find a way to link the request to the tag tags in the request chain for the object. For this project I'm sticking with tags, but that's a real flaw ...

0


source


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

unless you remove the __id part



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

      

+3


source


Unfortunately no, it's not prettier there. In fact, the actual solution is even uglier, but when all tags are stored in one text field, there is 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 above code assumes tags are separated by spaces. If not, you'll have to change your code to match how limited they are. The idea is that you use the delimiter as part of your search to make sure it is the actual tag and not just part of another tag.

If you don't want to do this, I would suggest switching to a different tagging system that doesn't dump them all into one textbox, django-taggit for example.

+2


source







All Articles