2 분 소요

QuerySet이란

DjangoORM

QuerySet은 Django ORM의 핵심 도구로, 데이터베이스와 상호작용할 때 사용되는 매우 간력한 도구입니다. 이 도구는 SQL 쿼리를 추상화하여 Python 코드로 쉽게 표현할 수 있게 해줍니다. 쉽게 설명해서 그림과 같이 파이썬 코드로 DB 작업을 할 수 있게 해줍니다.

QuerySet 생성

QuerySet은 Django 모델의 매니저 객체를 통해 생성됩니다. 일반적으로 매니저는 object라는 이름으로 사용됩니다. 예를 들어, Book 모델에서 모든 책을 가져오려면 다음과 같이 작성할 수 있습니다.

all_books = Book.objects.all()

해당 파이썬 코드는 아래와 같이 SQL Query문으로 바뀐 후 db에서 작업을 수행후 값을 다시 Python List 자료구조로 반환합니다.

select * from book;

필터링

데이터를 필터링하는 것입니다. SQL의 where 절에 해당합니다.

filter()

# status가 'available'인 책들만 필터링
available_books = Book.objects.filter(status='available')

exclude()

# status가 'unavailable'인 책들을 제외하고 필터링
non_unavailable_books = Book.objects.exclude(status='unavailable')

get()

# 특정 id의 책을 가져옴 (단일 객체)
book = Book.objects.get(id=1)

정렬

order_by() 매서드를 사용하여 QuerySet을 특정 필드를 기준으로 정렬할 수 있습니다.

# 출판 날짜를 기준으로 오름차순 정렬
books_by_date = Book.objects.order_by('published_date')

# 출판 날짜를 기준으로 내림차순 정렬
books_by_date_desc = Book.objects.order_by('-published_date')

슬라이싱 및 제한

Python의 리스트 슬라이싱과 유사하게 특정 범위의 데이터를 가져올 수 있습니다.

# 처음 10개의 책을 가져옴
first_ten_books = Book.objects.all()[:10]

# 10번째부터 20번째까지의 책을 가져옴
books_10_to_20 = Book.objects.all()[10:20]

값 추출 및 집계

aggregate()

전체 QuerySet에 대한 집계를 수행합니다.

from django.db.models import Count, Avg

# 전체 책의 평균 가격을 계산
average_price = Book.objects.aggregate(Avg('price'))

# 각 저자가 쓴 책의 수를 세는 예
author_book_count = Author.objects.aggregate(Count('book'))

annotate()

각 객체에 대해 집계 값을 계산하고 그 값을 추가합니다.

from django.db.models import Count

# 각 저자가 쓴 책의 수를 추가로 계산하여 반환. 즉, book_count 추가 필드 생성 
authors_with_book_count = Author.objects.annotate(book_count=Count('book'))

조인

Django에서는 외래 키(ForeignKey) 관계를 통해 두 개 이상의 모델을 연결할 수 있습니다.

# 저자의 이름을 통해 책을 필터링 (저자와 책은 ForeignKey로 연결되어 있다고 가정)
books_by_author = Book.objects.filter(author__name='J.K. Rowling')

지연 실행

QuerySet은 지연 실행을 지원합니다. 즉, QuerySet을 평가하기 전까지는 데이터베이스에 쿼리가 실행되지 않습니다. QuerySet을 평가하는 시점은 다음과 같습니다.

  • for 루프로 반복할때
  • list() 함수를 사용할때
  • len() 함수를 사용할때
  • bool() 함수를 사용할때
# 이 시점에서는 실제로 데이터베이스에 쿼리가 실행되지 않습니다.
books = Book.objects.filter(author__name='J.K. Rowling')

# for 루프가 시작되는 시점에 쿼리가 실행됩니다.
for book in books:
    print(book.title)

복잡한 쿼리 생성

QuerySet은 여러 쿼리셋 메서드를 체이닝하여 복잡한 쿼리를 만들 수 있습니다.

# 이름이 'J.K. Rowling'인 저자가 쓴, 2000년 이후에 출판된 책을 가져옴
books = Book.objects.filter(author__name='J.K. Rowling').filter(published_date__year__gt=2000).order_by('-published_date')

Raw SQL 쿼리 사용

때로는 Django ORM이 제공하는 기능을 넘어서는 복잡한 SQL 쿼리가 필요할 수 있습니다. 이때는 raw() 메서드를 사용해 직접 쿼리 작성이 가능합니다.

# 직접 SQL을 사용하여 책 데이터를 가져오기
books = Book.objects.raw('SELECT * FROM myapp_book WHERE published_date > %s', [2000])

파라미터 바인딩

raw() 매서드는 Django ORM에서 직접 SQL 쿼리를 실행할 때 사용하는 메서드입니다. 이 메서드는 SQL 쿼리를 직접 작성할 수 있게 해주지만, SQL Injection 공격을 방지하고 코드의 안정성을 유지 시켜주는 방법입니다.

  • 장점
    • 보안: 파라미터 바인딩을 사용하면 SQL 인젝션 공격에 대한 방어가 강화됩니다. 사용자가 입력한 값이 직접 SQL 쿼리에 삽입되지 않고, 데이터베이스 드라이버가 이를 안전하게 처리합니다.

    • 유지보수: 쿼리 문자열과 파라미터를 분리함으로써, 쿼리의 가독성과 유지보수성이 높아집니다.