
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을 확인하자
이 장에서는 프로젝트 데이터를 쿼리하는 여러 방법을 알아보았다.