Two Scoops of Django 3.x를 읽고 정리한 글입니다.
get_object_or_404()
이용하기Model.objects.get(pk=1)
→get_object_or_404(Model, pk=1)
# Example Use for ObjectDoesNotExist
from django.core.exceptions import ObjectDoesNotExist
from flavors.models import Flavor
from store.exceptions import OutOfStock
def list_flavor_line_item(sku):
try:
return Flavor.objects.get(sku=sku, quantity__gt=0)
except Flavor.DoesNotExist: # Flavor모델에 대해서만 예외처리
msg = 'We are out of {0}'.format(sku)
raise OutOfStock(msg)
def list_any_line_item(model, sku):
try:
return model.objects.get(sku=sku, quantity__gt=0)
except ObjectDoesNotExist: # 어떤 모델에서 예외가 발생하더라도 잡아준다
msg = 'We are out of {0}'.format(sku)
raise OutOfStock(msg)
from flavors.models import Flavor
from store.exceptions import OutOfStock, CorruptedDatabase
def list_flavor_line_item(sku):
try:
return Flavor.objects.get(sku=sku, quantity__gt=0)
except Flavor.DoesNotExist:
msg = 'We are out of {}'.format(sku)
raise OutOfStock(msg)
except Flavor.MultipleObjectsReturned:
msg = 'Multiple items have SKU {}. Please fix!'.format(sku)
raise CorruptedDatabase(msg)
복잡한 쿼리를 짧은 코드에 너무 많은 기능을 엮어서 작성하지 말자
# Don't do this!
from django.db.models import Q
from promos.models import Promo
def fun_function(name=None):
"""Find working ice cream promo"""
# 너무 길게 작성된 쿼리 체인이 화면이나 페이지를 넘겨버리게 되므로 좋지 않다
Promo.objects.active().filter(Q(name__startswith=name)|Q(description__icontain # ....
읽기 쉽게 작성하자
# Do this!
from django.db.models import Q
from promos.models import Promo
def fun_function(name=None):
"""Find working ice cream promo"""
results = Promo.objects.active()
results = results.filter(
Q(name__startswith=name) |
Q(description__icontains=name)
)
results = results.exclude(status='melted') # 주석을 달 수 있다!
results = results.select_related('flavors') # 줄마다 의미를 파악하지 좋다
return results
# Don't do this!
from models.customers import Customer
customers = []
for customer in Customer.objects.iterator(): # 루프를 돌며 고객 레코드 하나하나에 접근한다.
if customer.scoops_ordered > customer.store_visits:
customers.append(customer)
앞의 예제의 문제점
경합 상황(race condition): 공유 자원에 대해 여러 개의 프로세스가 동시에 접근을 시도하는 상태
from django.db.models import F
from models.customers import Customer
customers = Customer.objects.filter(scoops_ordered__gt=F('store_visits'))
# Query Expression Rendered as SQL
# SELECT * from customers_customer where scoops_ordered > store_visits
처음에는 인덱스 없이 시작하고 필요에 따라 추가해나간다.
Chapter 26: Finding and Reducing Bottlenecks에서 인덱스 분석에 대한 내용을 참고하자
# settings/base.py
DATABASES = {
'default': {
# ...
'ATOMIC_REQUESTS': True,
},
}
# flavors/views.py
from django.db import transaction
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.utils import timezone
from .models import Flavor
@transaction.non_atomic_requests
def posting_flavor_status(request, pk, status):
flavor = get_object_or_404(Flavor, pk=pk)
# 여기서 오토커밋 모드가 실행될 것이다. (장고 기본 설정)
flavor.latest_status_change_attempt = timezone.now()
flavor.save()
with transaction.atomic():
# 이 코드는 트랜잭션 안에서 실행된다.
flavor.status = status
flavor.latest_status_change_success = timezone.now()
flavor.save()
return HttpResponse('Hooray')
# If the transaction fails, return the appropriate status
return HttpResponse('Sadness', status_code=400)
목적 | ORM 메서드 | 트랜잭션을 이용할 것인가? |
---|---|---|
데이터 생성 | .create(), .bulk_create(), .get_or_create() | O |
데이터 가져오기 | .get(), .filter(), .count(), .it- erate(), .exists(), .exclude(), .in_bulk, etc. | X |
데이터 수정하기 | .update() | O |
데이터 지우기 | .delete() | O |
트랜잭션은 뷰에서만 적용된다. 스트림 응답이 SQL쿼리를 생성했다면? 오토커밋으로 동작한다.
MySQL을 사용한다면 table type을 확인하자
이 장에서는 프로젝트 데이터를 쿼리하는 여러 방법을 알아보았다.