쿼리 그 자체를 객체로 다룰 수 있게 하는 클래스이다.
모델 클래스 내에서 filter(), get() 등 쿼리셋, 인스턴스를 반환하는 메서드 내의 옵션(즉, 쿼리 조건문)을 동일하게 받는다.
# models.py
from django.db import models
class Product(models.Model):
sub_category = models.ForeignKey('SubCategory', on_delete=models.SET_NULL, null=True)
color = models.ForeignKey('Color', on_delete=models.SET_NULL, null=True, related_name='products')
name = models.CharField(max_length=45)
price = models.DecimalField(max_digits=10, decimal_places=2)
style_code = models.CharField(max_length=45)
origin = models.CharField(max_length=45)
manufacture_date = models.DateField()
description = models.TextField()
image_url = models.CharField(max_length=2000)
group = models.CharField(max_length=45)
class ProductOption(models.Model):
product = models.ForeignKey('Product', on_delete = models.CASCADE)
size = models.ForeignKey('Size', on_delete = models.CASCADE)
stock = models.PositiveIntegerField()
class Size(models.Model):
name = models.CharField(max_length=45)
위와 같은 모델에서,
from django.db.models import Q
from product.models import Product
Product.objects.get(id=1)
# <Product: Product object (1)>
q = Q(id=1)
q
# <Q: (AND: ('id', 1))>
Product.objects.get(q)
# <Product: Product object (1)>
Q 객체에는 연산자(&, |)를 사용하여 AND 조건과 OR 조건을 구분하여 추가할 수 있다. 이를 통해 원하는 형태의 조건 집합을 만들어 필터링 등에 활용할 수 있다.
또한, Django ORM Field Lookup(__gte, __range, __in, __icontains 등)를 그대로 사용할 수 있다.
q = Q()
q &= Q(id=2)|Q(id=3)
q &= Q(size=3)
q
# <Q: (AND: (OR: ('id', 2), ('id', 3)), ('size', 3))>
이는 (size=3)∩{(id=2)∪(id=3)} 의 조건 형태를 만든 것과 같다.
ForeignKey를 통해 관계 테이블에서 조건을 거는 서브쿼리의 경우, '참조하고자 하는 테이블__필드명' 으로 조건을 걸 수 있다.
가령, ProductOption 테이블(상품별,사이즈별 재고 데이터가 관리되는 테이블)에서 'aliquam'이라는 이름(name=> Product 테이블 필드)을 가진 상품의 사이즈별 재고 데이터들을 보고 싶다면 Q(product__name='aliquam') 이라는 쿼리 객체를 만들어 쿼리셋을 호출할 수 있다.
products = ProductOption.objects.filter(Q(product__name='aliquam'))
# products = <QuerySet [<ProductOption: ProductOption object (4)>, <ProductOption: ProductOption object (34)>, <ProductOption: ProductOption object (35)>, <ProductOption: ProductOption object (36)>, <ProductOption: ProductOption object (67)>, <ProductOption: ProductOption object (69)>]>
products.values('stock')
# <QuerySet [{'stock': 9}, {'stock': 8}, {'stock': 5}, {'stock': 2}, {'stock': 4}, {'stock': 9}]>
이런 방식은 역참조 관계에서도 동일하게 활용할 수 있다.
가령 Product 테이블에서 ProductOption 테이블을 통해 S 사이즈(size_id=1)가 있는 상품들만 보고 싶다면,
products = Product.objects.filter(Q(productoption__size=1))
# <QuerySet [<Product: Product object (1)>, <Product: Product object (2)>, <Product: Product object (4)>, <Product: Product object (5)>, <Product: Product object (6)>, <Product: Product object (7), '...(remaining elements truncated)...']>
S 사이즈가 없는 3번 상품은 쿼리셋에서 제외된 것이 보인다.