Django-filter: Filter for Django-Taggit (request)

Created on 16 Aug 2016  ·  11Comments  ·  Source: carltongibson/django-filter

Going by stars, django-taggit is about as popular as django-filter, and I believe it's the go-to solution for tagging in Django. It's been around since 2010, is regularly updated, and has several regular contributors.

What do you think about a django-taggit filter?

The closest I've got to this is:

tags = django_filters.ModelMultipleChoiceFilter(
        name='tags__name',
        to_field_name='name',
        conjoined=True,
        distinct=True,
        queryset=Tag.objects.all(),
    )

But I can't make it case-insensitive (a filter for tag 'TeSt' should return results with tag 'test').

Most helpful comment

Working example with latest libraries:

from taggit.forms import TagField
from django_filters.views import FilterView

class TagFilter(django_filters.CharFilter):
    field_class = TagField

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


class MyFilter(django_filters.FilterSet):
    tags = TagFilter(field_name='tags__name')

    class Meta:
        model = MyModel

All 11 comments

How would the TaggitFilter be different from a regular ModelMultipleChoiceFilter?

Also, I'm pretty sure we could add lookup_expr support here by simply joining the field name with the lookup.

With ModelMultipleChoiceFilter the URL has to be field_name=tag1&field_name=tag2, but there could be a significant number of tags, making the URL very long. Not ideal for a URL that could be shared, for example.

Tags are processed by django-taggit using a set of rules, allowing a single string to be passed containing all of the tags.

Regardless, lookup_expr in ModelMultipleChoiceFilter would definitely be helpful!

A quick look indicates that you should be able to use Taggit's TagsField. This would give you their csv parsing behavior.

class TagsFilter(CharFilter):
    field_class = taggit.forms.TagsField

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


class MyFilterSet(FilterSet):
    tags = TagsFilter(name='tags__name')

    class Meta:
        model = MyModel
GET http://localhost/api/my-model?tags=a,b,c,d

The caveat here is that this uses an in lookup, which is not the case-insensitive solution that you're looking for.

I'm more or less of the mind not to add a specific filter for Django Taggit, great package though it may be.

Ultimately creating project level filters isn't so hard. A decent gist would a long way. (Google is great for those.)

Happy to look at a A+ pull request if one turns up, but short of that I'm going to close this as out of scope.

OK, fair enough. What about support for lookup_expr in ModelMultipleChoiceFilter?

@stantond Happy to look at a PR. _Show me the Cödz_ 😀

I wish I could - unfortunately I'm not a skilled developer, but a fumbling PM!

+1

Working example with latest libraries:

from taggit.forms import TagField
from django_filters.views import FilterView

class TagFilter(django_filters.CharFilter):
    field_class = TagField

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


class MyFilter(django_filters.FilterSet):
    tags = TagFilter(field_name='tags__name')

    class Meta:
        model = MyModel

Use the Tag's slug field instead of the name field to be case insenstive. Taggit lower cases the slugs by default.

Note that we may expand the solution in https://github.com/carltongibson/django-filter/issues/460#issuecomment-507218839 with a custom field if we wish to support filter by a single tag that contains one or more whitespaces.

from django import forms
from django.utils.translation import gettext as _

from taggit.utils import parse_tags


class TagField(forms.CharField):

    def clean(self, value):
        value = super().clean(value)

        if "," not in value and '"' not in value and value:
            return [value]

        try:
            return parse_tags(value)
        except ValueError:
            raise forms.ValidationError(
                _("Please provide a comma-separated list of tags.")
            )

The code is extracted from taggit/forms.py.

Was this page helpful?
0 / 5 - 0 ratings