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