Django REST Framework中如何通过多个Medium ID过滤Artwork数据
Hey there! I see you're trying to fetch Artwork data by multiple medium_id values in DRF, and couldn't find a clear solution for many-to-many fields—let me walk you through a couple of straightforward approaches that work perfectly with your model code.
方案一:使用django-filter(推荐,简洁高效)
This is the most common and clean way to handle multi-value filtering in DRF. Here's how to set it up:
- First, install the library:
pip install django-filter
- Add it to your
INSTALLED_APPSand configure DRF to use its filter backend insettings.py:
INSTALLED_APPS = [ # ... your existing apps 'django_filters', ] REST_FRAMEWORK = { 'DEFAULT_FILTER_BACKENDS': [ 'django_filters.rest_framework.DjangoFilterBackend', # ... other backends you might be using ], }
- Create a filter class for your
Artworkmodel. This is where we define the multi-value filter for medium IDs:
from django_filters import rest_framework as filters from .models import Artwork class ArtworkFilter(filters.FilterSet): # Use BaseInFilter to accept multiple values via repeated query params medium_id = filters.BaseInFilter(field_name='mediums__id', lookup_expr='in') # Alternatively, use CommaSeparatedInFilter if you prefer comma-separated values: # medium_id = filters.CommaSeparatedInFilter(field_name='mediums__id', lookup_expr='in') class Meta: model = Artwork fields = ['medium_id']
- Attach this filter to your viewset:
from rest_framework import viewsets from .models import Artwork from .serializers import ArtworkSerializer # Assume you have this serializer from .filters import ArtworkFilter class ArtworkViewSet(viewsets.ModelViewSet): queryset = Artwork.objects.all() serializer_class = ArtworkSerializer filterset_class = ArtworkFilter
Now you can filter by multiple medium IDs like this:
- If using
BaseInFilter:your-domain.com/artworks/?medium_id=1&medium_id=2 - If using
CommaSeparatedInFilter:your-domain.com/artworks/?medium_id=1,2
方案二:自定义过滤(无需第三方库)
If you don't want to add another dependency, you can override the get_queryset method in your viewset to handle the filtering manually:
from rest_framework import viewsets from .models import Artwork from .serializers import ArtworkSerializer class ArtworkViewSet(viewsets.ModelViewSet): queryset = Artwork.objects.all() serializer_class = ArtworkSerializer def get_queryset(self): queryset = super().get_queryset() # Get all medium_id values from the query params medium_ids = self.request.query_params.getlist('medium_id') if medium_ids: # Filter artworks that are linked to any of the specified medium IDs queryset = queryset.filter(mediums__id__in=medium_ids).distinct() # Add .distinct() to avoid duplicate results (since many-to-many joins can create duplicates) return queryset
This works with the same URL format as the first approach: ?medium_id=1&medium_id=2.
Bonus: Filter artworks linked to ALL specified mediums
If you need artworks that are associated with every medium ID in your list (not just any), adjust the filtering logic like this:
def get_queryset(self): queryset = super().get_queryset() medium_ids = self.request.query_params.getlist('medium_id') if medium_ids: # Filter artworks that have all specified mediums for mid in medium_ids: queryset = queryset.filter(mediums__id=mid) queryset = queryset.distinct() return queryset
Or using Q objects for a more concise (but equivalent) approach:
from django.db.models import Q def get_queryset(self): queryset = super().get_queryset() medium_ids = self.request.query_params.getlist('medium_id') if medium_ids: query = Q() for mid in medium_ids: query &= Q(mediums__id=mid) queryset = queryset.filter(query).distinct() return queryset
内容的提问来源于stack exchange,提问作者Kiran S youtube channel

