Django 12. QuerySet API 1

jiffydev·2020년 10월 28일
0

소개

이전 포스팅에서는 장고의 QuerySet이 무엇인지를 아주아주 간단하게 알아봤다면, 이번에는 QuerySet에는 무엇이 있고 어떤 식으로 사용하는지 알아보고자 한다. 이 포스팅에서 사용한 데이터들은 이곳이 원 출처임을 밝혀 둔다.
한편 쿼리셋 메소드는 종류가 많고 그 기능도 여러가지이므로 여기서 다 다루기는 어렵다. 이 포스팅에서는 기본적인 메소드 위주로 언급하고, 언급되지 않은 메소드에 대해서는 공식문서를 확인하면 더 자세히 알 수 있다.

새 QuerySet을 반환하는 메소드 1탄

1. filter(**kwargs)

장고의 기초라도 봤다면 누구나 알 만한 메소드이다. 키워드 인자로 검색할 필드와 값을 주면 그 조건에 맞는 쿼리셋을 돌려주고, 없다면 빈 쿼리셋을 돌려준다.

>>> Product.objects.filter(product_owned_company_id=1)
<QuerySet [<Product: Product object (19)>, <Product: Product object (40)>, <Product: Product object (68)>]>
>>> Product.objects.filter(product_owned_company_id=1, name='product_name19')
<QuerySet [<Product: Product object (19)>]>
>>> Product.objects.filter(price=1)
<QuerySet []>

하나 이상의 매개변수를 넣어서 검색할 수 있는데, 둘 이상이라면 SQL문의 AND와 같은 방식으로 join된다. 그래서 만약 OR 조건으로 복수의 매개변수를 넣고 싶다면 Q object를 사용해야 한다.(Q object에 대해서는 다른 글에서 다루겠다.)

2. annotate(*args, **kwargs)

이 개념을 이해하는데 몇 시간이 걸렸는지 모르겠다. 이제 겨우 2번인데 집에 갈 시간이 다가온다.
annotate의 사전적 의미는 '주석을 달다' 이다. 하지만 우리는 뭔가를 추가한다, 특히 '새로운 필드를 추가한다'고 생각하는 것이 이해하기 편할 것 같다. 비록 데이터베이스에는 보이지 않지만 분명 새로운 항목이 추가된 것을 확인할 수 있기 때문이다.
우선 이 개념을 이해하려고 할 때 테이블 하나로는 부족하다. 왜냐하면 annotate를 사용해 price 필드의 min,max,sum,count,avg 등을 구해도 결국 한 row에만 적용되는 것이기 때문에 annotate 안에서 무엇을 구해도, 한 row의 price필드의 값과 같게 나오기 때문이다.
그래서 참조 관계에 있는 테이블을 사용해야 한다. 여기서는 Product 테이블과 Company 테이블이다. Product 테이블에서 Company를 FK로 참조하는 상태이고, Product에는 가격과 회사의 id 필드가 존재한다.
같은 회사에도 여러 종류의 물건이 있을텐데, 우리는 회사 id가 1번인 회사의 물건들 중 최대, 최소, 평균을 구해볼 것이다.(객체들 중 가장 첫번째[0]가 id=1이다)

>>> min_price=Company.objects.annotate(Min('product__price'))
>>> vars(min_price[0]){'_state': <django.db.models.base.ModelState object at 0x7fed3c7fef40>, 'id': 1, 'name': 'company_name1', 'tel_num': '070-123-4567', 'address': '서초구 ~~ 마제스타시티', 'product__price__min': 66408}

>>> max_price=Company.objects.annotate(Max('product__price'))
>>> vars(max_price[0]){'_state': <django.db.models.base.ModelState object at 0x7fed3c7feb80>, 'id': 1, 'name': 'company_name1', 'tel_num': '070-123-4567', 'address': '서초구 ~~ 마제스타시티', 'product__price__max': 179120}

>>> avg_price=Company.objects.annotate(Avg('product__price'))
>>> vars(avg_price[0]){'_state': <django.db.models.base.ModelState object at 0x7fed3c7fe640>, 'id': 1, 'name': 'company_name1', 'tel_num': '070-123-4567', 'address': '서초구 ~~ 마제스타시티', 'product__price__avg': 117979.3333}

1번 회사의 최대, 최소, 평균 가격이 잘 나온 것을 확인할 수 있다.

>>> prices=Company.objects.annotate(max_p=Max('product__price'), min_p=Min('product__price'), avg_p=Avg('product__price'))
>>> vars(prices[0])
{'_state': <django.db.models.base.ModelState object at 0x7fed3c7fe6a0>, 'id': 1, 'name': 'company_name1', 'tel_num': '070-123-4567', 'address': '서초구 ~~ 마제스타시티', 'max_p': 179120, 'min_p': 66408, 'avg_p': 117979.3333}

최대, 최소, 평균 가격을 한번에 구하고, 너무 길었던 필드명도 kwargs로 넣음으로써 짧게 줄일 수 있다.

이 영상에서 annotate를 구현하는 방법에 대해 많은 도움을 받았다.

3. values(*fields, **expressions)

지정한 필드와 값의 딕셔너리를 리턴하는 쿼리셋을 리턴한다. 필드를 지정하지 않으면 모든 필드와 값이 나오게 된다.

>>> Product.objects.values('price', 'name')
<QuerySet [{'price': 92214, 'name': 'product_name1aa'}, {'price': 91443, 'name': 'product_name2'}]>

>>> Product.objects.values()
<QuerySet [{'id': 1, 'name': 'product_name1aa', 'price': 92214, 'product_owned_company_id': 26}, {'id': 2, 'name': 'product_name2', 'price': 91443, 'product_owned_company_id': 35}]>

4. values_list(*fields, flat=False, named=False)

values()와 비슷하지만, 리턴값이 튜플이란 점이 다르다. 그리고 튜플 안에는 필드명은 없고 오로지 값만 들어있다.

>>> Product.objects.values_list('price', 'name')
<QuerySet [(92214, 'product_name1aa'), (91443, 'product_name2'), (17229, 'product_name3')]>

>>> Product.objects.values_list()
<QuerySet [(1, 'product_name1aa', 92214, 26), (2, 'product_name2', 91443, 35), (3, 'product_name3', 17229, 17)]>

매개변수 중 flat 옵션은 넘겨줄 필드가 하나일 때만 True로 사용 가능하다. flat=True로 하게 되면 값을 튜플이 없는 하나의 값으로 리턴하게 된다.

>>> Product.objects.values_list('price')
<QuerySet [(92214,), (91443,), (17229,), (34326,), (55961,)]>

>>> Product.objects.values_list('price', flat=True)
<QuerySet [92214, 91443, 17229, 34326]>

이렇게 flat=True로 하면 반복문을 돌릴 때도 별다른 처리 없이 쉽게 돌릴 수 있어 자주 사용하게 될 것 같다.

profile
잘 & 열심히 살고싶은 개발자

0개의 댓글