본문으로 건너뛰기
김신건의 로그

[DRF] Filtering: django-filter, SearchFilter, OrderingFilter

· 수정 · 📖 약 1분 · 275자/단어 #drf #filter #search #ordering #django-filter
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 / icontainsLIKE
startswith / endswithprefix/suffix
gt, gte, lt, lte크기 비교
infield__in
range범위
date, year, month, week날짜 부분
isnullNULL 여부
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, ...]
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필터링
DRFdjango-filter, SearchFilter
Railsransack, filterrific
Spring DataSpecification, Querydsl
FastAPI수동 Query params
NestJSnestjs-typeorm-crud

흔한 함정

WARNING

  1. filter_fields (옛 이름) = deprecated. filterset_fields.
  2. 큰 filterset_class + 많은 필드 = OpenAPI schema 폭발.
  3. search_fields 에 큰 컬럼 = 매 요청 full-table scan. GIN 인덱스.
  4. 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…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기