carltongibson / django-filter

A generic system for filtering Django QuerySets based on user selections
https://django-filter.readthedocs.io/en/main/
Other
4.45k stars 769 forks source link

Filter for Django-Taggit (request) #460

Closed stantond closed 8 years ago

stantond commented 8 years ago

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').

rpkilby commented 8 years ago

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.

stantond commented 8 years ago

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!

rpkilby commented 8 years ago

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.

carltongibson commented 8 years ago

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.

stantond commented 8 years ago

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

carltongibson commented 8 years ago

@stantond Happy to look at a PR. Show me the Cödz 😀

stantond commented 8 years ago

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

Atorich commented 7 years ago

+1

baolsen commented 5 years ago

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
nuarhu commented 5 years ago

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

kiawin commented 4 years ago

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.

GorlikItsMe commented 2 weeks ago

If you are looking for working tag filter (like me) where you have list of all avaible tags you can use this:

tags = django_filters.ModelMultipleChoiceFilter(
        field_name='tags__name',
        to_field_name='name',
        distinct=True,
        queryset=Tag.objects.all(),
        label="Tags"
    )

or this cool version where only tags you ever used are showed:

import django_filters
from django.forms import CheckboxSelectMultiple
from taggit.models import Tag

tags = django_filters.ModelMultipleChoiceFilter(
        field_name='tags__name',
        to_field_name='name',
        # conjoined=True, # if true all selected tags must be added to page
        distinct=True,
        queryset=Tag.objects.exclude(contentpage=None).all(),
        label=_("Tags")
        widget=CheckboxSelectMultiple,
    )