장고 Q를 활용한 필터링, 정렬, 검색 방법

2022. 2. 20. 23:16·Django

장고의 filter()에서 처리하는 키워드 아규먼트는 기본적으로 & (AND) 형식입니다. | (OR)와 같은 보다 복잡한 쿼리를 처리하려면 Q 객체를 사용할 수 있습니다. Q 객체(django.db.models.Q)는 키워드 아규먼트를 처리하기 위한 객체입니다.

Q 객체 사용 방법

Q 객체를 사용하기 위해서는 장고의 모델에서 제공하는 Q를 임포트해야 합니다.

from django.db.models import Q

각 키워드 아규먼트를 차례로 Q 객체에 넘겨줄 수 있습니다. 이때 Q 객체의 각 아규먼트는 | (OR)로 구분되지 않는 경우, & (AND)로 이어지게 됩니다.

Poll.objects.get(
    Q(question__startswith='Who'),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))

한편, Q 객체를 사용하는 경우에는 Q 객체를 사용하지 않는 다른 키워드 아규먼트보다 먼저 처리되어야 합니다.

Poll.objects.get(
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    question__startswith='Who',
)

Q를 사용한 복합 필터링

다음과 같이 Q 객체를 사용하여 복합적인 필터링을 구현할 수 있습니다. 이는 HTTP GET 요청을 통해 파라미터와 값을 처리하고, 해당하는 값이 존재하게 될 경우, Q()에 q.add(...)를 추가하는 방식입니다. Q()는 Accomodations.objects.all()과 동일한 기능을 합니다. 마지막의 q.AND 는 q.OR이 될 수도 있으며, 이는 각 조건을 어떻게 연결 지을 지에 따라 달라집니다.

class AccommodationListView(View):
    def get(self, request, *args, **kwargs):
        try:
            accommodations = Accommodation.objects.all()

            thema_group = request.GET.getlist('themaGroup', None)
            region = request.GET.get('region', None)
            is_verified = request.GET.get('isVerified', None)
            people = request.GET.get('people', 1)
            sort = request.GET.get('sort', None)
            search = request.GET.get('search', None)

            q = Q()

            if thema_group:
                q.add(Q(themagroup__name__in=thema_group), q.AND)

            if region:
                q.add(Q(region=region), q.AND)

            if is_verified:
                q.add(Q(is_verified=is_verified), q.AND)

            accommodations = Accommodation.objects.filter(q, available_people__range=[people,30])

            (...)

코드를 자세히 보시면 조금 다른 부분들이 있습니다. 먼저, thema_group의 경우에는 하나의 값이 아닌 여러 값(예를 들면, ?themaGroup=도심&themaGroup=바다)이 들어올 수 있기 때문에 이를 GET요청에서 .getlist로 처리하고, 이와 관련된 ThemaGroup 모델의 name에 thema_group의 값으로 들어온 것이 있는지를 필터링하도록 되어 있습니다. Q(themagroup__name__in=thema_group) 부분이 바로 이러한 조건을 처리하기 위한 코드입니다.

필터링 결과 정렬하기

Q()를 통한 필터링 조건을 쿼리셋에 filter(q)와 같은 방식으로 담았다면, 이제 order_by 메소드를 사용하여 이를 간단히 정렬해볼 수 있습니다.

if sort == 'newest':
    accommodations = accommodations.order_by('created_at')
elif sort == "highest":
    accommodations = accommodations.order_by('-price')
elif sort == "lowest":
    accommodations = accommodations.order_by('price')

검색 기능 구현

검색 기능 또한 구현할 수 있습니다. GET 요청으로 serach 파라미터와 값을 받은 다음 icontains를 활용하여 검색 기능을 제공할 수 있습니다.

if search:
    accommodations = accommodations.filter(
    name__icontains = search
)

최종: 필터링 + 정렬 + 검색 기능

다음은 이상의 방법을 모두 조합하여 제가 작성했던 뷰 파일입니다. 숙소의 리스트를 필터링 요청을 따라 제공하고, 숙소 리스트에 대해 정렬 및 검색 기능을 제공합니다.

class AccommodationListView(View):
    def get(self, request, *args, **kwargs):
        try:
            accommodations = Accommodation.objects.all()

            thema_group = request.GET.getlist('themaGroup', None)
            region = request.GET.get('region', None)
            is_verified = request.GET.get('isVerified', None)
            people = request.GET.get('people', 1)
            sort = request.GET.get('sort', None)
            search = request.GET.get('search', None)

            q = Q()

            if thema_group:
                q.add(Q(themagroup__name__in=thema_group), q.AND)

            if region:
                q.add(Q(region=region), q.AND)

            if is_verified:
                q.add(Q(is_verified=is_verified), q.AND)

            accommodations = Accommodation.objects.filter(q, available_people__range=[people,30])

            if search:
                accommodations = accommodations.filter(
                    name__icontains = search
            )

            if sort == 'newest':
                accommodations = accommodations.order_by('created_at')
            elif sort == "highest":
                accommodations = accommodations.order_by('-price')
            elif sort == "lowest":
                accommodations = accommodations.order_by('price')

            accommodation_information = [
                {   
                    "id": accommodation.id,
                    "name": accommodation.name,
                    "description": accommodation.description,
                    "detail_description": accommodation.detail_description,
                    "price": accommodation.price,
                    "address": accommodation.address,
                    "region": accommodation.region,
                    "is_verified": accommodation.is_verified,
                    "latitude": accommodation.latitude,
                    "longtitude": accommodation.longtitude,
                    "check_in_time": accommodation.check_in_time,
                    "check_out_time": accommodation.check_out_time,
                    "available_people": accommodation.available_people,
                    "image_url": [images.image_url for images in accommodation.accommodationimage_set.all()],
                    "thema_group": [thema.name for thema in accommodation.themagroup_set.all()]
                } for accommodation in accommodations]

            return JsonResponse({"message": accommodation_information}, status=200)
        except:
            return KeyError({"message":"KEY_ERROR"}, status=400)

 

참고 자료:

https://docs.djangoproject.com/en/4.0/topics/db/queries/#complex-lookups-with-q
https://velog.io/@jxxwon/Django-Q-%EA%B0%9D%EC%B2%B4

반응형

'Django' 카테고리의 다른 글

장고 개발 환경에 따라 settings.py 분리하는 방법  (0) 2022.03.13
장고 get_or_create()와 race condition 문제  (0) 2022.03.05
장고에서 HTTP GET, POST 요청 처리하는 방법  (0) 2022.02.20
장고 ORM과 쿼리셋의 개념  (0) 2022.02.07
장고, 파이썬 csv 파일 처리 방법  (0) 2022.02.01
'Django' 카테고리의 다른 글
  • 장고 개발 환경에 따라 settings.py 분리하는 방법
  • 장고 get_or_create()와 race condition 문제
  • 장고에서 HTTP GET, POST 요청 처리하는 방법
  • 장고 ORM과 쿼리셋의 개념
휘 Hwi
휘 Hwi
개발자 성장 로그
  • 휘 Hwi
    개발자 로그: 변화를 위한 공간
    휘 Hwi
  • 전체
    오늘
    어제
    • 분류 전체보기 (61)
      • 101 (1)
      • Web | Internet (4)
      • HTML | CSS (4)
      • Python (9)
      • Django (20)
      • Javascript (0)
      • Node.js (0)
      • React (0)
      • React Native (0)
      • Database (1)
      • Git (1)
      • Terminal | Vim (1)
      • Auth | Security (4)
      • AWS (0)
      • Docker (0)
      • Kubernetest (1)
      • Deployment (1)
      • Project (2)
      • TIL (12)
  • 블로그 메뉴

    • 홈
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    깃 ssh
    장고 프로젝트
    요소 절대 크기
    정규 표현식
    HTTP POST 요청
    새 탭에서 열기
    깃 퍼블릭 키 등록
    TIL
    target="_blank"
    html
    요소 크기 설정
    HTTP GET 요청
    프로젝트 회고
    JWT
    함수
    요소 상대 크기
    장고 URL
    장고 Views
    JWT 디코딩
    JWT 인코딩
    탭내빙
    깃 오류
    배포
    장고
    깃 에러 해결
    파라미터
    HTTP
    파이썬
    깃
    css
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
휘 Hwi
장고 Q를 활용한 필터링, 정렬, 검색 방법
상단으로

티스토리툴바