Reputation: 4944
I have a tagging system in place for a model that my API exposes. The models look something like this:
class TaggableModel(models.Model):
name = models.CharField(max_length=255)
tags = models.ManyToManyField(Tag, related_name="taggable_models")
class Tag(models.Model):
tag = models.CharField(max_length=32)
I've then set up a serializer and view that look like:
class TaggableModelSerializer(serializers.ModelSerializer):
class Meta:
model = TaggableModel
fields = ('id', 'name', 'tags',)
read_only_fields = ('id',)
class TaggableModelViewSet(viewsets.ModelViewSet):
queryset = TaggableModel.objects.all()
serializer_class = TaggableModelSerializer
permission_classes = (AllowAny,)
filter_backend = [DjangoFilterBackend]
filterset_fields = ['tags']
If I want to grab all TaggableModels
that have tag ids 1, 2, or 3, I can do so via:
https://my-api-domain/api/taggable-models?tags=1&tags=2&tags=3
Is there a way to split on a delimiter, so I can pass it all as one parameter? e.g.:
https://my-api-domain/api/taggable-models?tags=1,2,3
It looks like I can write my own custom DjangoFilterBackend filters, but I am a bit unsure as to where to start. Or perhaps there is an easier way to accomplish this?
Upvotes: 5
Views: 2684
Reputation: 4346
There is an even simpler way to achieve this using the django-filter
package. Deep within the django-filter
documentation, it mentions that you can use "a dictionary of field names mapped to a list of lookups".
Your code would be updated like so:
# views.py
from django_filters.rest_framework import DjangoFilterBackend
class TaggableModelViewSet(viewsets.ModelViewSet):
queryset = TaggableModel.objects.all()
serializer_class = TaggableModelSerializer
permission_classes = (AllowAny,)
filter_backend = [DjangoFilterBackend]
filterset_fields = {
'tags': ["in", "exact"] # note the 'in' field
}
Now in the URL you would add __in
to the filter before supplying your list of parameters and it would work as you expect:
https://my-api-domain/api/taggable-models?tags__in=1,2,3
The django-filter
documentation on what lookup filters are available is quite poor, but the in
lookup filter is mentioned in the Django documentation itself.
Upvotes: 1
Reputation: 3805
Sure you can do this by having custom filterset class with specific field 'widget' (that's how it is called in django-filters)
Here's a sample you can try:
# filters.py
from django_filters.rest_framework import FilterSet, filters
from django_filters.widgets import CSVWidget
from .your_models import Tag, TaggableModel
class TaggableModelFilterSet(FilterSet):
tags = filters.ModelMultipleChoiceFilter(
queryset=Tag.objects.all(), widget=CSVWidget,
help_text=_("A list of ids, comma separated, identifying tags"),
method='filter_tags'
)
class Meta:
model = TaggableModel
fields = ['tags']
def filter_tags(self, queryset, name, value):
if value:
queryset = queryset.filter(tags__in=value)
return queryset
# views.py
class TaggableModelViewSet(viewsets.ModelViewSet):
queryset = TaggableModel.objects.all()
serializer_class = TaggableModelSerializer
permission_classes = (AllowAny,)
filter_backends = [DjangoFilterBackend]
filter_class = TaggableModelFilterSet
Upvotes: 6