#Need help modeling tags and filtering using (django-taggit and django-filters)

8 messages · Page 1 of 1 (latest)

thin stratus
#

Hello!

My model currently looks something like this:

#models.py
class Part(models.Model):
    title = models.CharField(max_length=70, unique=True)
    category = models.IntegerField(choices=PartTypes.choices(), default=PartTypes.DEFAULT)
    pictures = models.CharField(max_length=200)
    tags = TaggableManager()
#filters.py
class Tagfilter(filters.CharFilter):
    field_class = TagField

    def __init__(self, *args, **kwargs):
        kwargs.setdefault('lookup_expr', 'in')
        super().__init__(*args, **kwargs)

Currently, my Part model supports different categories let's say car, truck, and suv for simplicity.
Each category basically has a field tags (djang-taggit) that allows me to add any tags I want.

I have a UI where the user can filter based on:

First set of filters: (check boxes)
-car
-truck
-suv
Second set of filters: (check-boxes)
-AWD
-4x4
-FWD
-RWD

The expected behavior is that if multiple filters in the same set are checked like (cars, suv) then the request should return cars OR suv only.

However, the issue is when the user selects (cars, suv, and AWD) it currently returns cars OR suv OR AWD. The expected user experience should be like (cars OR suv) AND (AWD).

I am not sure if I need to redo my models.. or I can somehow fix this issue with the way I implement my filters.

Any suggestions?

Thanks!

thin stratus
#

Can't seem to figure out why this doesn't work

input = car+awd

class Tagfilter(filters.CharFilter):
    field_class = TagField

    def __init__(self, *args, **kwargs):
        kwargs.setdefault('lookup_expr', 'in')
        super().__init__(*args, **kwargs)

    def filter(self, qs, value):
        if not value:
            return qs

        value = value[0]
        print('value')
        print(value)
        filterGroups = value.split(' ')

        query = None
        final_query = None

        for groups in filterGroups:
            fields = groups.split(',')
            for field in fields:
                print(field)
                q_objects = Q(tags__slug=field)
                if query is None:
                    query = q_objects
                else:
                    query |= q_objects
            
            if final_query is None:
                final_query = query
            else:
                final_query &= query
            
            query = None

        print('query')
        print(final_query)

        return qs.filter(final_query)```


final_query is (AND: ('tags__slug', 'car'), ('tags__slug', 'awd'))

I have a car that has tags: ['car','awd'] but my query returns an empty list
#

I think I don't need to override the init and kwargs now. (I removed it) However, it still doesn't work which is weird

#

Object has: "tags": ["linear", "5 pin"]

>>> from django.db.models import Q
>>> q_objects = Q(tags__slug='car')
>>> q_objects &= Q(tags__slug='awd')
>>> q_objects
<Q: (AND: ('tags__slug', 'car'), ('tags__slug', 'awd'))>
>>> from partpicker.models import Car
>>> Car.objects.all().filter(q_objects)
<QuerySet []>```
thin stratus
#

okay, I kind of replaced my actual types with fake types so the next snippet has the real types in my db if it gets confusing but

>>> q_objects
<Q: (AND: ('tags__slug', 'linear'), ('tags__slug', '5-pin'))>
>>> Part.objects.all().filter(tags__slug='linear')
<QuerySet [<Part: Part object (2)>, <Part: Part object (27)>, <Part: Part object (29)>, <Part: Part object (30)>]>
>>> Part.objects.all().filter(tags__slug='5-pin')
<QuerySet [<Part: Part object (29)>, <Part: Part object (30)>, <Part: Part object (31)>, <Part: Part object (32)>]>
>>> Part.objects.all().filter(q_objects)
<QuerySet []>

This should return 29 and 30 no?

#

If I do it manually it actually works which is good:

>>> linear_parts = Part.objects.all().filter(tags__slug='linear')
>>> pin_parts = Part.objects.all().filter(tags__slug='5-pin')
>>> linear_parts & pin_parts
<QuerySet [<Part: Part object (29)>, <Part: Part object (30)>]>
thin stratus
thin stratus
#

got it to work but probably not the most optimal..

    def filter(self, qs, value):
        if not value:
            return qs

        value = value[0]
        filterGroups = value.split('+')

        query = None
        query_list = []

        for groups in filterGroups:
            fields = groups.split(',')
            for field in fields:
                print(field)
                q_objects = Q(tags__slug=field)
                if query is None:
                    query = q_objects
                else:
                    query |= q_objects

            query_list.append(query)    
            query = None

        results = []

        for query in query_list:
            results.append(qs.filter(query))

        final_result = results.pop()

        for result in results:
            final_result &= result

        return final_result