DRF 过滤与排序 #

一、过滤概述 #

1.1 过滤类型 #

过滤类型 说明
精确过滤 字段值完全匹配
模糊过滤 包含、开头、结尾匹配
范围过滤 数值、日期范围
搜索过滤 多字段全文搜索

1.2 过滤后端 #

python
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.SearchFilter',
        'rest_framework.filters.OrderingFilter',
    ],
}

二、DjangoFilterBackend #

2.1 安装 #

bash
pip install django-filter

2.2 基本配置 #

python
INSTALLED_APPS = [
    ...
    'django_filters',
]

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
    ],
}

2.3 简单过滤 #

python
from django_filters.rest_framework import DjangoFilterBackend

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['category', 'author', 'is_published']

请求:

text
GET /api/articles/?category=1&is_published=true

2.4 自定义FilterSet #

python
import django_filters
from .models import Article

class ArticleFilter(django_filters.FilterSet):
    title = django_filters.CharFilter(lookup_expr='icontains')
    content = django_filters.CharFilter(lookup_expr='icontains')
    category__name = django_filters.CharFilter(lookup_expr='icontains')
    
    created_at__gte = django_filters.DateTimeFilter(field_name='created_at', lookup_expr='gte')
    created_at__lte = django_filters.DateTimeFilter(field_name='created_at', lookup_expr='lte')
    
    class Meta:
        model = Article
        fields = ['category', 'author', 'is_published']

使用:

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_class = ArticleFilter

2.5 过滤器类型 #

python
class ArticleFilter(django_filters.FilterSet):
    title = django_filters.CharFilter(lookup_expr='icontains')
    author__username = django_filters.CharFilter(lookup_expr='icontains')
    
    views__gte = django_filters.NumberFilter(field_name='views', lookup_expr='gte')
    views__lte = django_filters.NumberFilter(field_name='views', lookup_expr='lte')
    
    created_at__year = django_filters.NumberFilter(field_name='created_at', lookup_expr='year')
    created_at__month = django_filters.NumberFilter(field_name='created_at', lookup_expr='month')
    
    is_published = django_filters.BooleanFilter()
    category = django_filters.ModelChoiceFilter(queryset=Category.objects.all())
    tags = django_filters.ModelMultipleChoiceFilter(
        field_name='tags__id',
        queryset=Tag.objects.all()
    )
    
    class Meta:
        model = Article
        fields = []

三、SearchFilter #

3.1 基本用法 #

python
from rest_framework.filters import SearchFilter

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    filter_backends = [SearchFilter]
    search_fields = ['title', 'content']

请求:

text
GET /api/articles/?search=关键词

3.2 搜索字段类型 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    filter_backends = [SearchFilter]
    search_fields = [
        'title',           # 包含匹配
        '=title',          # 精确匹配
        '^title',          # 开头匹配
        '$title',          # 结尾匹配
        '@title',          # 全文搜索(PostgreSQL)
        'content__icontains',
        'author__username',
        'category__name',
    ]

3.3 自定义搜索参数 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    filter_backends = [SearchFilter]
    search_fields = ['title', 'content']
    search_param = 'q'

请求:

text
GET /api/articles/?q=关键词

四、OrderingFilter #

4.1 基本用法 #

python
from rest_framework.filters import OrderingFilter

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    filter_backends = [OrderingFilter]
    ordering_fields = ['created_at', 'views', 'title']
    ordering = ['-created_at']

请求:

text
GET /api/articles/?ordering=-views,created_at

4.2 允许所有字段 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    filter_backends = [OrderingFilter]
    ordering_fields = '__all__'
    ordering = ['-created_at']

4.3 自定义排序 #

python
class CustomOrderingFilter(OrderingFilter):
    def filter_queryset(self, request, queryset, view):
        ordering = self.get_ordering(request, queryset, view)
        
        if ordering:
            if '-popularity' in ordering:
                queryset = queryset.annotate(
                    popularity=Count('likes') + Count('comments') * 2
                )
            return queryset.order_by(*ordering)
        
        return queryset

五、组合过滤 #

5.1 多后端组合 #

python
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['category', 'is_published']
    search_fields = ['title', 'content']
    ordering_fields = ['created_at', 'views']
    ordering = ['-created_at']

5.2 请求示例 #

text
GET /api/articles/?category=1&is_published=true&search=关键词&ordering=-views

六、自定义过滤 #

6.1 自定义过滤后端 #

python
from rest_framework.filters import BaseFilterBackend

class IsPublishedFilterBackend(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        if not request.user.is_staff:
            return queryset.filter(is_published=True)
        return queryset

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    filter_backends = [IsPublishedFilterBackend]

6.2 动态过滤 #

python
class DynamicFilterBackend(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        params = request.query_params
        
        for param, value in params.items():
            if hasattr(queryset.model, param):
                queryset = queryset.filter(**{param: value})
        
        return queryset

七、最佳实践 #

7.1 性能优化 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.select_related(
        'author', 'category'
    ).prefetch_related(
        'tags'
    )
    serializer_class = ArticleSerializer
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]

7.2 缓存过滤结果 #

python
from django.core.cache import cache

class CachedFilterBackend(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        cache_key = f'articles_{request.query_params.urlencode()}'
        result = cache.get(cache_key)
        
        if result is None:
            result = list(queryset.values_list('id', flat=True))
            cache.set(cache_key, result, 60 * 5)
        
        return queryset.filter(id__in=result)

八、总结 #

本章学习了DRF过滤与排序:

  • DjangoFilterBackend:精确过滤
  • SearchFilter:全文搜索
  • OrderingFilter:排序
  • 自定义过滤:自定义过滤逻辑
  • 最佳实践:性能优化

让我们继续学习异常处理!

最后更新:2026-03-28