F 객체와 Q 객체를 활용한 고급 동적 쿼리 작성법

Django ORM에서 F()와 Q() 객체를 활용하여 데이터베이스 레벨에서 효율적인 필드 연산과 복잡한 동적 쿼리 조건을 구성하는 방법

F() 객체: 데이터베이스 레벨 필드 참조

F() 객체의 핵심 개념

  • 모델의 필드 값 자체를 나타내는 객체

  • 파이썬 메모리가 아닌 데이터베이스에서 직접 필드 값 참조 및 연산

  • SQL 쿼리로 변환될 때 해당 필드의 열 이름을 나타냄

필드 값 비교

# 재고가 최소 재고 수준보다 적은 상품 조회
low_stock_products = Product.objects.filter(stock_quantity__lt=F('minimum_stock_level'))

Race Condition 방지를 위한 원자적 업데이트

# 조회수 증가 (안전한 방식)
Product.objects.filter(pk=1).update(hit_count=F('hit_count') + 1)

annotate()와 함께 계산 필드 생성

# 할인율 적용한 최종 가격 계산
products_with_final_price = Product.objects.annotate(
    final_price=F('price') * (1.0 - F('discount_rate'))
)

Q() 객체: 복잡한 쿼리 조건 구성

Q() 객체의 핵심 개념

  • SQL의 WHERE 절을 캡슐화하는 객체

  • OR, AND, NOT 논리 연산자 조합으로 복잡한 쿼리 조건 생성

  • filter()의 콤마 구분은 AND 조건만 가능, OR 조건은 Q() 객체 필수

논리 연산자

  • & (AND): 두 조건을 모두 만족

  • | (OR): 두 조건 중 하나라도 만족

  • ~ (NOT): 조건을 만족하지 않음

OR 조건 쿼리 예시

# 제목에 'Django' 포함하거나 내용에 'Python' 포함한 게시물
Post.objects.filter(
    Q(title__icontains='Django') | Q(content__icontains='Python')
)

동적 쿼리 빌딩

def product_search(request):
    query = Q(is_active=True)
    
    keyword = request.GET.get('q')
    if keyword:
        query &= (Q(name__icontains=keyword) | Q(description__icontains=keyword))
    
    category_id = request.GET.get('category')
    if category_id:
        query &= Q(category_id=category_id)
    
    min_price = request.GET.get('min_price')
    if min_price:
        query &= Q(price__gte=min_price)
    
    products = Product.objects.filter(query)

F()와 Q() 객체 조합 활용

복합 조건 쿼리

# 재고량이 주문량보다 많고 특정 카테고리인 상품
products = Product.objects.filter(
    Q(category=target_category) & Q(stock_quantity__gt=F('order_count'))
)

커스텀 Manager/QuerySet 활용

class ProductQuerySet(models.QuerySet):
    def low_on_stock(self):
        return self.filter(stock_quantity__lt=F('minimum_stock_level'))
    
    def search(self, keyword):
        if not keyword:
            return self
        return self.filter(Q(name__icontains=keyword) | Q(description__icontains=keyword))

class Product(models.Model):
    objects = ProductQuerySet.as_manager()

Last updated