티스토리 뷰
지난 번 내가 Django + Restframework에서 지원하는 검색기능으로 filter_fields와 search_fields를 이야기하면서 나의 멘탈이 증발한 것을 이야기했다.
뭐, 나쁜 기능은 아니다. 나름 개발 속도를 높이기 위한 방법으로 약간 ㅂㅅ같은 길로 들어섰을뿐 아주 단순한 방법으로 아주 빠르게 자동으로 기능을 구현해주지만 성능은 책임지지 않는 뭔가 반쪽짜리 API를 만들어도 괜찮을 때는 괜찮은 방법이다.
하지만!! 그런데!! 이런 것으로는 안되는게, 만약에 높은 성능을 요구하는 프로젝트에서 Django로 프로젝트를 진행하는 경우는 드물지만 만약에 진행을 하게 된다면 온 사방에서 성능 이슈로 문제들이 펑펑 터지는 일이 생기게 될 거라는 사실이 너무나도 분명하다.
먼저 이야기했던 filter_fields와 search_fields로는 감당할 수 없는 문제들이 계속해서 발생하게 된다면 이 기능들은 너무나도 불완전하기 그지 없고 나 또한 이러한 사실들 때문에 Django를 포기하려고 하는 찰나 Restframework 문서를 읽다가 내가 미처 읽지 못했던 기능을 알게 되었는데 번거롭고 여러 메서드를 오버라이딩 해야하지만 자유도가 높은 "filter를 그냥 손으로 적용"하는 방법이다.
뭐든지 백문이 불여일견이라고 일단 코드를 먼저 짜보도록 하자.
from django.shortcuts import render
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
from rest_framework.filters import SearchFilter
from rest_framework.response import Response
from .serializers import BoardTypeSerializer
from .models import BoardType
# Create your views here.
class BoardListCreateView(ListCreateAPIView):
name = "board-list-create"
serializer_class = BoardTypeSerializer
def get_queryset(self):
queryset = BoardType.objects.all()
return queryset
def list(self, request, *args, **kwargs):
queryset = self.set_filters( self.get_queryset(), request )
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
def set_filters(self, queryset, request):
type = request.query_params.get('type', None)
description = request.query_params.get('description', None)
if type is not None:
queryset = queryset.filter(type=type)
if description is not None:
queryset = queryset.filter(description__contains=description)
return queryset
class BoardRetrieveUpdateDestroyAPIView(RetrieveUpdateDestroyAPIView):
name = "board-retrieve-update-destroy"
serializer_class = BoardTypeSerializer
queryset = BoardType.objects.all()
전체 코드는 위와 같다.
일단 코드를 살펴보면 이전의 BoardListCreateView 클래스와 좀 달라졌다는 것을 알아볼 수 있을거다. 일단 그 거지같은 filter_fields와 search_fields를 지웠다. 그리고 queryset 속성을 지우고 그 대신 get_queryset 메서드를 만들었다. 음.. 만들었다기 보다는 상속받은 ListCreateAPIView 에 있는 get_queryset 메서드를 오버라이딩 한거라고 생각하면 되겠다. 그와 비슷하게 list 메서드도 마찬가지다.
set_filters 메서드는 상속이 아니라 내가 그냥 만들었다. 저런 방식으로 filter를 적용하면 된다고 알려주려고.
그럼 set_filters 메서드만 따로 떼서 알아보도록 하자.
def set_filters(self, queryset, request):
type = request.query_params.get('type', None)
description = request.query_params.get('description', None)
if type is not None:
queryset = queryset.filter(type=type)
if description is not None:
queryset = queryset.filter(description__contains=description)
return queryset
일단 처음에 하는 일이 request객체에 있는 query_param 을 통해 'type'과 'description' 값이 넘어왔는지 확인하는 절차를 밟는다.
어차피 이 함수는 GET 방식으로 통신하므로 모든 파라미터는 Query String을 통해 전달되니 query_params 객체를 이용해 찾는 것이 바람직하겠다.
그 다음은 type이 None이 아닌지, description이 None이 아닌지 확인하고 필터를 적용하는데 여기서 잘 봐야할 것은 filter를 적용하는 방법이다.
뭐 type 적용하는 부분은 무난하게 이해할거라고 본다. type = type 이렇게 적용하면 매칭검색을 수행하게된다. 문제는 "__contains" 라고 어미를 붙인 부분이다. 저건 LIKE 검색을 수행하기 위해 붙이는 어미다.
Django에는 저런 어미를 붙여서 IN, LIKE, OR 등등의 기능을 구현하는데 자세한 내용은 여기를 클릭하면 되겠다. 내용이 너무 많아서 따로 포스팅을 하진 않겠지만 필요한 기능들은 거의 다 저 링크에 있으니 참고하는게 도움이 될거다.
아무튼 저렇게 filter를 적용하면 기본적으로 AND 로 묶이게 되는데 Post man 으로 요청을 보내면 아래와 같이 쿼리를 생성해내는 것을 확인 할 수 있다.
딱 내가 원하는 방식대로 쿼리를 생성해낸 filter 기능이었다. 필요할 때는 매칭검색, 필요할 때는 LIKE 검색을 할 수 있는 코드를 만들어 내었다.
아, 속이 시원해!
더 많은 기능은 Django 메뉴얼을 참고하시고 모르는 문제는 댓글을 달면 답변주겠다.
그러면 이제 검색은 대충 됐으니까 다음시간부터는 페이징을 시작하도록 하겠다.
안녕!
'Dev > Python' 카테고리의 다른 글
Django API 연동하기 - 2. 검색과 페이징 #4 LimitOffsetPagination (0) | 2018.06.13 |
---|---|
Django API 연동하기 - 2. 검색과 페이징 #3 PageNumberPagination (0) | 2018.06.13 |
Django API 연동하기 - 2. 검색과 페이징 #1 (0) | 2018.06.05 |
Django API 연동하기 - 1. 준비운동 (0) | 2018.06.01 |
Django CORS 설정과 API 연동 (0) | 2018.05.31 |
- TAG
- API서버, api연동, Django, filter_fields, python, Python Django, restframework, search_fields, 장고