[Python] Query Parameter

김상웅·2022년 7월 5일
0

[파이썬]

목록 보기
16/17

✅ Query Parameter


RESTful API에 대한 포스팅을 하면서 path parameterquery parameter에 대해 언급한 적이 있습니다.
보러가기

실제 url을 통해 HTTP 통신을 할 때 필요한 요소인데요.

query parameter는 단순히 특정 리소스에 대한 정보를 요청할 때 응답하는 방식이 아닙니다.

즉 사용자가 원하는 조건에 따른 리소스를 정렬, 검색할 때 사용한다고 배웠는데요.

Django 프로젝트에서 어떻게 활용되는지 알아보겠습니다.



✅ query parameter를 받아오자


클라이언트는 HTTP 요청을 할 때 특정 정보를 검색하거나 정렬하기 위해 url에 객체형태의 값을 넣어 보냅니다.

다음과 같이 말이죠.

http://sangwoong/users?age=27&name=sangwoong

위의 요청을 분석하면 다음과 같습니다.

request      value
-------------------------------------------------------------
users        유저들의 정보들 중
?age=27      age라는 key의 value가 27인 정보
&name=woong  그리고 name이라는 key의 value가 sangwoong이라는 정보

그렇다면 요청을 하고 있는 url에서 parameter 객체를 가져오는 방법을 알아보겠습니다.


📌 request.get()


request라는 이름을 가진 객체에 Python의 get() 메서드를 사용하는 방법입니다.

다음과 같은 request(요청)이 있다고 가정해봅시다:

http://sangwoong/users?age=27&name=woong

이 요청에 대해 get() 메서드를 활용하여 값을 얻어보면 다음과 같습니다.

request.get("age")  >> AttributeError: 'WSGIRequest' object has no attribute 'get'
request.get("name") >> AttributeError: 'WSGIRequest' object has no attribute 'get'

Python에 의해 정의되는 request는 사전형 객체입니다.

그렇기 때문에 django에서 request은 view를 정의할 때 인수로써 설정하는 사전형 데이터가 아니기 때문에 다음과 같은 에러가 발생하는 것입니다.


📌 requset.GET[]


request(요청)에 Django의 GET[] 메서드를 사용하는 방법입니다.

다음과 같은 request(요청)이 있다고 가정해봅시다:

http://sangwoong/users?age=27&name=woong

이 요청에 대해 get() 메서드를 활용하여 값을 얻어보면 다음과 같습니다.

request.GET["age"]  >> 27
request.GET["name"] >> woong

📌 request.GET.get()


바로 위에서 언급했듯 request.GET["key"] 메서드를 활용한다면,

"key" 에 대한 값("value)을 반환할 것입니다.

마지막에 get() 메서드를 추가한다면, "key"가 존재하지 않을 때의 값을 설정해 줄 수 있습니다.

마찬가지로 다음과 같은 요청이 있다고 가정해봅시다:

http://sangwoong/users?age=27&name=woong

다음 메서드를 통해 값을 가져오면 다음과 같습니다.

request.GET.get("age")  >> 27
request.GET.get("name") >> woong

만약 존재하지 않는 key에 접근한다면 결과는 다음과 같습니다.

request.GET.get("phone_number")                >> None (default)
request.GET.get("phone_number", 010-0000-0000) >> 010-0000-0000 (default)


✅ 정보를 찾아서

📌 &와 filter()


다시 한번 리마인드를 하자면 우리는 query parameter를 특정 조건을 통해 정렬, 검색, 필터랑 할 때 사용한다고 배웠습니다.

django 프로젝트의 views 안에서 작성되는 ORM 중 우리는 get(), filter(), all() 등의 메서드를 사용할 수 있는데요.

특정 조건들이 서로 맞물려 있는 경우 & 연산으로 필터링을 할 수 있습니다.

다음처럼 말이죠.

요청:

http://sangwoong/users?age=27&name=woong&phone_number=01012345678

필터링 로직:

age          = request.GET.get("age")
name         = request.GET.get("name")
phone_number = request.GET.get("phone_number")

users = User.objects.filter(age = age, name = name, phone_number = phone_number)

위에서는 & 연산 하나만 사용하여 filter()라는 메서드를 활용할 수 있었습니다.

만약 다음과 같이 코드를 작성했을 때 발생할 수 있는 문제점이 있는데요.

  1. parameter 값이 존재하지 않는 경우 의도한 대로 데이터를 반환할 수 없습니다.
    age, name, phone_number 중 하나라도 전달되지 않으면 빈배열을 반환할 수 있게 됩니다.

  2. 조건이 점점 늘어나는 경우 filter() 메서드로 받는 인자의 수가 늘어나게 됩니다.
    이는 가독성 문제 뿐만 아니라 예상했던 로직 결과와 달라 의도한 대로 데이터를 반환할 수 없게 될 것입니다.


📌 Q()


다양한 연산 조건을 효율적으로 처리하기 위해 Q() 객체를 사용할 수 있습니다.

Django 공식문서 - filter()Django 공식문서 - Q()에 명시된 내용은 다음과 같습니다.

(의역.. 요약..)

filter() 함수는 and 조건을 통해 필터링을 할 수 있지만,
or 연산이 포함되거나 더 복잡한 조건을 필터링할 때 Q() 객체를 사용할 수 있습니다.

Q 객체는 키워드 인자 (query parameter)를 하나의 집합 으로 묶을 때 사용합니다.
 >>  ( "{key: value}" )
 
Q 객체는 &와 | 연산자를 통해 객체를 하나의 조건으로 이어줄 수 있습니다.
 >> ( "{key: value} and {key: value } ... " )

📌 Q() 사용방법


Django 프로젝트에서의 사용 방법은 다음과 같습니다 (views.py):

# 우선 Q를 사용하기 위해 import를 해줍니다.
from django.db.models import Q

from django.views import View

# product의 list를 요청하는 경우
# 요청 url http://12.34.45.8:8000/products?category_id=1&color="red"
class ProductView(View):
	def get(self, request):
    	# query parameter를 받아옵니다. 값이 없다면 기본 값은 None 값입니다.
        category_id   = request.GET.get("category_id")
        product_color = request.GET.get("product_color")
        
        # q에 Q() 객체를 저장합니다. 기본은 비어있는 값입니다.
        q = Q()
        
        # 참조하는 테이블 혹은 필드의 이름이 받아온 query 값과 일치여부를 판단합니다.
        if category_id :
        	q &= Q(category = category_id)
       
       	# 두개 이상의 조건을 &= 연산을 통해 q에 & 조건으로 묶어 사용할 수 있습니다.
       	if product_color :
        	q &= Q(product_color = product_color)
            
       	# 현재 q에는 query parameter 객체 2개가 들어 있습니다.
        # filter() 함수의 인자로 사용할 수 있습니다.
       	products = Product.objects.filter(q)
        
        ...

만약 category_idproduct_color에 대한 값이 없는 경우가 있을 수 있습니다.

이때 q는 빈 값으로 filter() 함수의 인자로 전달되는데요.

직접 프로젝트를 진행하다보면 해당 데이터베이스의 전체 데이터를 불러오는 것을 알 수 있습니다.



📌 &= |= 연산자


&=

위의 예제에서 다음 연산자를 사용한 것을 볼 수 있습니다.

q라는 변수에 Q() 객체를 담는데 여러 조건이 있으면 해당 조건들을 and 연산으로 묶습니다.


|=

해당 연산자는 사용한 적이 없는데요.

만약 or 연산을 통해 조건을 필터링하거나 검색해야하는 경우 Q() 객체를 다음 표현식을 사용하여 조건들을 묶을 수 있습니다.



📌 이외의 다른 방법


  1. and 조건을 통해 필터링 하는 경우 filter() 함수에서 query paramater 인자를 ,로 구분할 수 있습니다.

  2. 좀 더 복잡한 조건을 다루기 위해 Q() 객체를 사용할 수 있습니다.

  3. 이번에는 좀 다른 방법을 알아보겠습니다.

우선 코드부터 보겠습니다:

from django.views import View

# product의 list를 요청하는 경우
# 요청 url http://12.34.45.8:8000/products?category_id=1&color="red"
class ProductView(View):
	def get(self, request):
    	
        # query parameter의 key에 해당하는 value를 오른쪽 변수에 할당
        filter_set {
        	"category_id"   = "category_id",
            "product_color" = "product_color"
        }
        
        # filter 객체에 query parameter { key : value } 할당
        filter = {
        	filter_set.get(key) : value for key, value in filter_set.items() if filter_set.get(key)
        }
        
        # 언패킹을 통해 filter의 key:value를 인자로 받아 필터링
       	products = Product.objects.filter(**filter)
        
        ...

Q() 객체를 사용하면 두번의 조건문을 사용해야 했습니다.

위의 코드처럼 사용하게 되면,

1) query parameter로 받아오는 key 값이 filter_set으로 설정한 key와 동일한 값이 있는지 판단을 합니다.

2) 있다면 2-1) 로직을 수행합니다.

2-1) filter_set의 모든 key와 value에 대해 filter 객체에는 filter_set에 있는 key와 value 값이 남게 됩니다.

2-2) query parameter와 일치하는 key 값이 없다면 모든 품목 `all()`을 반환합니다.

3) filter 객체에 남은 모든 key와 value를 언패킹을 통해 filter() 메서드의 인자로 넣어 필터링을 합니다.



이처림 이번 포스팅에서는 필터링을 하는 다양한 방법을 소개해드렸습니다.

어떤 조건을 어떻게 필터링을 해야 효율적이고 가독성이 좋은 코드가 될 지 고민을 많이 해볼 수 있었고, 다양한 코드를 배워 재밌었습니다.

필터링 조건을 2~3개가 아닌 5개 이상이 되었을 때 어떤 코드가 효율적인지 다양한 프로젝트에 적용을 해보면 좋을 것 같습니다.

profile
누구나 이해할 수 있도록

0개의 댓글