django-filter 라이브러리로 강력한 필터링 기능 손쉽게 추가하기

Django DRF에서 복잡한 if문 분기 처리 대신 선언적 방식으로 API 필터링 기능을 구현하는 방법

왜 django-filter가 필요한가?

Before: 수동 필터링

def get_queryset(self):
    queryset = super().get_queryset()
    status = self.request.query_params.get('status')
    author_username = self.request.query_params.get('author_username')
    
    if status:
        queryset = queryset.filter(status=status)
    if author_username:
        queryset = queryset.filter(author__username__icontains=author_username)
    
    return queryset

After: django-filter 사용

# filters.py
class PostFilter(django_filters.FilterSet):
    class Meta:
        model = Post
        fields = ['status', 'author__username']

# views.py
class PostViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Post.objects.all()
    filterset_class = PostFilter

설치 및 기본 설정

설치

pip install django-filter

settings.py 설정

INSTALLED_APPS = [
    'rest_framework',
    'django_filters',
]

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

FilterSet으로 필터 정의

기본 사용법

class PostFilter(django_filters.FilterSet):
    class Meta:
        model = Post
        fields = ['status', 'author']

검색 조건 커스터마이징

class PostFilter(django_filters.FilterSet):
    class Meta:
        model = Post
        fields = {
            'title': ['exact', 'icontains'],
            'created_at': ['gte', 'lte'],
        }

사용 예시:

  • GET /api/posts/?title__icontains=django

  • GET /api/posts/?created_at__gte=2025-06-20

고급 필터링

명시적 Filter 클래스 사용

class PostFilter(django_filters.FilterSet):
    title_contains = django_filters.CharFilter(field_name='title', lookup_expr='icontains')
    created_range = django_filters.DateFromToRangeFilter(field_name='created_at')
    status = django_filters.ChoiceFilter(choices=Post.STATUS_CHOICES)
    
    class Meta:
        model = Post
        fields = []

커스텀 메소드 필터

class PostFilter(django_filters.FilterSet):
    tags = django_filters.CharFilter(method='filter_by_tags')
    
    def filter_by_tags(self, queryset, name, value):
        tag_names = value.split(',')
        return queryset.filter(tags__name__in=tag_names).distinct()

실무 팁

성능 최적화

  • N+1 문제 방지를 위해 ViewSet에서 select_related/prefetch_related 사용

queryset = Post.objects.all().select_related('author')

보안 주의사항

  • Meta.fields = '__all__' 절대 사용 금지

  • 허용할 필드만 명시적으로 선언

코드 구조화

  • 각 앱에 filters.py 파일 생성하여 FilterSet 클래스들을 관리

Last updated