I have two models, one defining users, the other defining labels on these users. I am using Django REST Framework to create an API. I would like to be able to query users containing at least the label ids 1 and 2.
For instance if the user's labels are:
[(1,2), (1,2,3), (2,3), (1,3)]
I want the query to return
[(1,2), (1,2,3)]
.
So far, I've managed to query users with a given label (let's say id=1) by doing: /api/users/?labels=1
, but I am unable to query users with labels 1 and 2. I've tried /api/users/?labels=1,2
or /api/users/?labels=1&labels=2
but it returns some invalid users, i.e. users without labels 1 or 2...
Github test repo:
https://github.com/TheDimLebowski/drf-m2m-filter
Code:
class Label(models.Model):
name = models.CharField(max_length = 60)
class User(models.Model):
labels = models.ManyToManyField(Label)
class UserFilter(django_filters.FilterSet):
labels = django_filters.filters.BaseInFilter(
name='labels',
lookup_type='in',
)
class Meta:
model = User
fields = ('labels',)
class LabelSerializer(serializers.ModelSerializer):
class Meta:
model = Label
fields = ('id','name')
class UserSerializer(serializers.ModelSerializer):
labels = LabelSerializer(many = True)
class Meta:
model = User
fields = ('labels',)
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = (filters.DjangoFilterBackend,)
filter_class = UserFilter
filter_fields = ('labels',)
The simplest way to filter the queryset of any view that subclasses GenericAPIView is to override the . get_queryset() method. Overriding this method allows you to customize the queryset returned by the view in a number of different ways.
The DjangoFilterBackend class is used to filter the queryset based on a specified set of fields. This backend class automatically creates a FilterSet (django_filters. rest_framework. FilterSet) class for the given fields.
Q object encapsulates a SQL expression in a Python object that can be used in database-related operations. Using Q objects we can make complex queries with less and simple code. For example, this Q object filters whether the question starts wiht 'what': from django. db.
You can chain filters. For example, if you need all users which labels contain both 1
and 2
values, you can write a query like so:
User.objects.filter(labels=1).filter(labels=2)
django-filters
does not support queries like this by default so you need a custom filter.
class M2MFilter(django_filters.Filter):
def filter(self, qs, value):
if not value:
return qs
values = value.split(',')
for v in values:
qs = qs.filter(labels=v)
return qs
class UserFilter(django_filters.FilterSet):
labels = M2MFilter(name='labels')
class Meta:
model = User
fields = ('labels',)
Now you can write labels id's comma-separated and get exactly what you need
/api/users/?labels=1,2
Here is good answer about m2m queries
I have the same question and I found something like this:
from django.db.models import Q
from rest_framework import viewsets
class YourViewSet(viewsets.ModelViewSet)
def get_queryset(self):
# get lable infos here
lables_info = self.request.query_params.get('lable')
lables = lables_info.split(',')
lable1, lable2 = lables[0], lables[1]
return models.objects.fliter(
Q(lable=lable1) | Q(lable=lable2)
)
.......
Django==2.2.4
djangorestframework==3.10.2
django-filter==2.2.0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With