장고의 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 |