[DRF] Filtering: django-filter, SearchFilter, OrderingFilter
DRF filtering, django-filter, SearchFilter, OrderingFilter, FilterSet, filter_backends
정의
Filtering = 쿼리 파라미터로 결과 제한. 3가지 backend + custom.
3가지 내장 Backend
flowchart TD
Q{Filter Backend}
Q --> DF["DjangoFilterBackend<br/>(django-filter, 완전 필터)"]
Q --> SF["SearchFilter<br/>(전문 검색 like)"]
Q --> OF["OrderingFilter<br/>(정렬)"]
설치 + 설정
pip install django-filter
INSTALLED_APPS = [..., 'django_filters']
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
'rest_framework.filters.SearchFilter',
'rest_framework.filters.OrderingFilter',
]
}
1. DjangoFilterBackend
간단한 필드 매칭
class ArticleList(generics.ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
filterset_fields = ['author', 'category', 'is_published']
GET /articles/?author=1&category=tech&is_published=true
상세 FilterSet
import django_filters
class ArticleFilter(django_filters.FilterSet):
title = django_filters.CharFilter(lookup_expr='icontains')
min_views = django_filters.NumberFilter(field_name='views', lookup_expr='gte')
max_views = django_filters.NumberFilter(field_name='views', lookup_expr='lte')
published_after = django_filters.DateFilter(field_name='published_at', lookup_expr='gte')
tag = django_filters.CharFilter(method='filter_tag')
ordering = django_filters.OrderingFilter(
fields=(
('title', 'title'),
('views', 'views'),
('published_at', 'published_at'),
)
)
class Meta:
model = Article
fields = ['author', 'category']
def filter_tag(self, queryset, name, value):
return queryset.filter(tags__name__iexact=value)
class ArticleList(generics.ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
filterset_class = ArticleFilter
GET /articles/?title=django&min_views=100&published_after=2026-01-01&tag=python
Lookup expressions
| Lookup | 의미 |
|---|---|
exact (기본) | field=x → WHERE field = x |
iexact | 대소문자 무시 |
contains / icontains | LIKE |
startswith / endswith | prefix/suffix |
gt, gte, lt, lte | 크기 비교 |
in | field__in |
range | 범위 |
date, year, month, week | 날짜 부분 |
isnull | NULL 여부 |
regex / iregex | 정규식 |
2. SearchFilter
class ArticleList(generics.ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
filter_backends = [filters.SearchFilter]
search_fields = ['title', 'body', 'author__username', 'tags__name']
GET /articles/?search=django+rest
Prefix 조정
| Prefix | 의미 |
|---|---|
| 없음 | icontains (기본) |
^ | istartswith |
= | iexact |
@ | full text search (Postgres) |
$ | iregex |
search_fields = ['=username', '^title', '@body']
3. OrderingFilter
class ArticleList(generics.ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
filter_backends = [filters.OrderingFilter]
ordering_fields = ['created_at', 'views', 'title']
ordering = ['-created_at'] # 기본 정렬
GET /articles/?ordering=-views
GET /articles/?ordering=title,-views # 다중
다중 Backend 조합
class ArticleList(generics.ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
filter_backends = [
DjangoFilterBackend,
filters.SearchFilter,
filters.OrderingFilter,
]
filterset_class = ArticleFilter
search_fields = ['title', 'body']
ordering_fields = ['created_at', 'views']
GET /articles/?category=tech&search=django&ordering=-views
Custom Filter Backend
from rest_framework import filters
class IsOwnerFilterBackend(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
return queryset.filter(owner=request.user)
class ArticleList(generics.ListAPIView):
filter_backends = [IsOwnerFilterBackend, ...]
PostgreSQL Full-Text Search
from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank
class ArticleList(generics.ListAPIView):
serializer_class = ArticleSerializer
def get_queryset(self):
q = self.request.query_params.get('q', '')
if q:
vector = SearchVector('title', weight='A') + SearchVector('body', weight='B')
query = SearchQuery(q)
return Article.objects.annotate(
rank=SearchRank(vector, query),
).filter(rank__gte=0.1).order_by('-rank')
return Article.objects.all()
자세한 건 postgresql 의 full-text.
OpenAPI 문서화
django-filter 는 OpenAPI schema 자동:
filterset_class = ArticleFilter
# → OpenAPI 에 파라미터 자동 추가
자세한 건 drf-schemas-openapi.
다른 프레임워크
| Framework | 필터링 |
|---|---|
| DRF | django-filter, SearchFilter |
| Rails | ransack, filterrific |
| Spring Data | Specification, Querydsl |
| FastAPI | 수동 Query params |
| NestJS | nestjs-typeorm-crud |
흔한 함정
WARNING
- filter_fields (옛 이름) = deprecated.
filterset_fields. - 큰 filterset_class + 많은 필드 = OpenAPI schema 폭발.
search_fields에 큰 컬럼 = 매 요청 full-table scan. GIN 인덱스.ordering사용자 입력 신뢰 = 임의 필드 정렬 → 성능.ordering_fields화이트리스트.
관련 위키
이 글의 용어 (6개)
- [DB Internals] GIN Index 깊이: 구조, jsonb_path_ops, tsvectordatabase-internals
- 정의 GIN (Generalized Inverted Index) = 한 값이 여러 항목 을 가지는 데이터 (배열, JSON, tsvector) 의 역색인. PostgreSQL 의…
- [DB] PostgreSQL: 프로세스 모델, MVCC, WAL, 확장성database-internals
- 정의 PostgreSQL 은 오픈소스 ORDBMS. 1986 UC Berkeley POSTGRES 의 후예. MVCC, 확장 가능 타입, JSONB, full-text searc…
- [Django] QuerySet과 Managerdjango
- 정의 QuerySet은 DB 쿼리를 lazy하게 빌드하는 객체. , 같은 메서드를 체이닝해도 실제 SQL 실행은 평가 시점( , , , slice 등)에 한 번 발생. Manag…
- [DRF] Pagination + Throttlingdjango
- 정의 Pagination = 큰 목록을 페이지 단위로 나눔. Throttling = 요청 빈도 제한. 둘 다 API 안정성의 기본. Pagination 3종 언제 어느 것을 쓸까…
- [DRF] Schemas + OpenAPI: 자동 문서화django
- 정의 OpenAPI (전 Swagger) = REST API를 기계 가독 스펙으로 기술하는 표준. DRF는 이 스펙을 ViewSet / Serializer 로부터 자동 추출할 수…
- [DRF] Views: APIView, GenericViews, Mixins, FBVdjango
- 정의 DRF View 4가지 스타일. 자유도 vs 자동화 트레이드오프. 4가지 스타일 | 스타일 | 코드량 | 자유도 | |---|---|---| | | 유사 Django FBV…
💬 댓글