Closed stantond closed 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.
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
.
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,
)
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:
But I can't make it case-insensitive (a filter for tag 'TeSt' should return results with tag 'test').