create
, update
, filter
, order by
, annotation
, aggreagate
의 일부로 사용할 수 있는 값 혹은 표현식을 일컫는다. 경합상황이란?
다중 프로그래밍 시스템이나 다중 처리기 시스템에서 두 명령어가 동시에 같은 기억 장소를 액세스할 때 그들 사이의 경쟁에 의해 수행 결과를 예측할 수 없게 되는 것.
# 절대 따라하지 말 것
from models.customers import Customer
customers = []
for customer in Customer.objects.iterator():
if customer.scoops_ordered > customer.store_visits:
customers.append(customer)
from django.db.models import F
from models.customers import Customer
customers = Customer.objects.filter(scoops_ordered__gt=F('store_visits'))
위 코드 쿼리표현식 중 F() expressions의 기능
SELECT * from customers_customer where scoops_ordered > store_visits
F()
객체는 모델 필드 혹은 어노테이트된 열의 값을 나타낸다. 데이터베이스에서 파이썬 메모리로 데이터를 갖고오지 않고 모델 필드 값을 참조해 사용하여 데이터베이스 작업을 수행할 수 있다. 장고는 F()
객체를 사용하여 데이터베이스 수준에서 필요한 작업을 설명하는 SQL 표현식을 생성하게된다.
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed += 1
reporter.save()
위 파이썬 구문은 reporter.stories_filed의 값을 데이터베이스에서 메모리로 갖고와 파이썬 연산자를 이용해서 조작하고 데이터베이스에 저장한것이다.
하지만 이 작업을 다음과 같이 할 수도 있다.
from django.db.models import F
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()
reporter.stories_filed = F('stories_filed') + 1
는 값을 인스턴스 속성에 할당 한 것 처럼 보이지만 데이터베이스에 대한 연산을 설명하는 SQL 구문이다. Django는 F()
의 인스턴스를 만나면 파이썬 연산자를 오버라이딩하여 캡슐화된 SQL 표현식을 생성한다.
이 경우 reporter.stories_filed
가 나타내는 데이터베이스 필드를 증가 시키도록 데이터베이스에 지시한다. reporter.stories_filed
나 다른 어떠한 값이 있어도 데이터베이스에 의해 처리되기 때문에 파이썬은 알지 못한다.
위 값으로 저장한 새 값을 얻으려면 다시 호출해야한다.
reporter = Reporters.objects.get(pk=reporter.pk)
F()
의 또다른 유용한 이점은 데이터베이스가 필드 값을 업데이트하면 race condition을 피할 수 있다는 점이다.
첫번째 예시(F() 미사용)는 두개의 파이썬 thread를 사용해서 실행하고 A라는 thread가 값을 받아 파이썬 메모리에 저장할 때, B 라는 thread는 값을 추출, 증가, 저장할 수 있다. A 라는 thread는 값을 증가, 저장하게 되면 B라는 thread는 작업했던 내용이 손실된다. A는 B의 작업 이전에 값을 갖고왔기 때문이다.
F()
를 사용하면 위 문제를 해결할 수 있다. 메모리에서 갖고온 값을 기반으로 작업하는 게 아니라 save() 나 update()가 실행될 때, 데이터베이스의 필드 값을 기준으로 작업하기 때문이다.
SELECT a, b, c, d
FROM test
WHERE a=c
위와 같은 쿼리가 있따면 F()
를 사용해서 아래와 같이 표현 가능하다.
from django.db.models import F
Test.objects.filter(a=F('c'))
SELECT a,b,c,d as dddd
FROM test
위와 같이 d라는 컬럼의 이름을 dddd로 바꿀 때, F()를 사용해서 아래와 같이 표현 가능하다.
from django.db.models import F
Test.objects.annotate(dddd=F('d'))
from django.db.models import F
Company.object.order_by(F('last_contacted').desc(nulls_last=True))
Company 모델 인스턴스들을 last_contacted 필드 기준으로 내림차순 정렬하되, 해당 필드가 null이면 제일 뒤로 보낸다는 의미의 코드이다.
nulls_first
옵션이다 .asc()
메서드도 있다.