두잇 두두 2024. 1. 24. 18:06
728x90

F()

F()는 model field의 값, 변환된 값, 주석이 달린 열을 나타냅니다. 모델 field의 값을 참조하고 실제로 db에서 python 메모리로 가져오지 않고 이를 사용해 db 작업을 할 수 있습니다.

대신 Django는 F()를 사용해서 db 수준에서 필요한 작업을 설명하는 SQL 표현식을 생성합니다.

reporter = Reporters.objects.get(name='Tintin")
reporter.stories_field += 1
reporter.save()

위 코드에서 db에서 reporter.stories_field의 값을 메모리로 끌어오고 python 연산자를 이용해서 작업한 뒤 다시 개체를 db에 저장했습니다.

from django.db.models improt F

reporter = Reporters.objects.get(name="Tintin")
reporter.stories_field = F("stories_field") + 1
reporter.save()

reporter.stories_FILED = F('stories_field') + 1 은 python에서 instance 속성에 값을 할당하는 것처럼 보이지만 실제로 db에 대한 작업을 설명하는 SQL 구문입니다.

Django는 F() instance를 발견하면 pytthon 연산자를 재정의하여 캡슐화된 SQL 표현식을 만듭니다. 이 경우 reporter.stories_field가 나타내는 db 필드를 증가시키도록 db에 지시하는 것입니다.

이러한 방식으로 저장된 새 값을 액세스 하려면 객체를 다시 로드 해야합니다.

reporter = Reporters.objects.get(pk=reporter.pk)
# 더 간결히
reporter.refresh_from_db()

 

single instance에 사용될 뿐만 아니라 F()는 update()를 통해 객체 인스턴스의 QuerySet에 사용될 수 있습니다. 이는 위 두 개의 쿼리 get() 및 save()를 하나로 줄입니다.

reporter = Reporters.objects.filter(name="Tintin")
reporter.update(stories_filed=F("stories_filed") + 1)

update()를 사용해서 여러 객체의 필드 값을 증가시킬 수 있습니다. 이는 db에서 객체를 모두 Python으로 가져와 반복하고 각 객체의 filed를 증가시킨 다음 db에 다시 저장하는 것보다 훨씬 빠릅니다.

Reporter.objects.update(stories_filed=F("stories_filed") + 1)

F()는 Python이 아닌 db가 작업하도록 하기, 일부 작업에 필요한 쿼리 수 줄이기에 이점이 있습니다.


F()를 이용한 race conditions 피하기

F()의 또 다른 이점은 Python이 아닌 필드 값을 업데이트 하면 race condition을 피할 수 있습니다.

두개의 python threads가 실행 된다 가정하면 첫 번째 스레드가 필드 값을 검색, 증가 시킨 뒤 저장할 때 두 번째 스레드도 저장 한다면 첫 번째 스레디의 작업은 사라집니다.

db가 필드 업데이트를 담당하는 경우 위와 같은 경우가 사라지게 됩니다.


F() assignments persist after Model.save()

F() 할당은 Model.save() 이후에도 지속됩니다. 예를 들어

reporter = Reporters.objects.get(name="Tintin")
reporter.stories_field = F("stories_field") +1 
reporter.save()

reporter.name = "Tintin Jr."
reporter.save()

이 경우 store_field가 두 번 업데이트 됩니다. 처음 1이면 최종 값은 3이 됩니다. 

model을 save 한 후 다시 로드하면(Refresh_from_db()) 이러한 지속성을 피할 수 있습니다.


Using F() in filters

F()는 Python이 아닌 필드 값을 기반으로 한 기준에 따라 개체 집합을 필터링 할 수 있는 QuerySet필터에서도 매우 유용합니다.

Filter can reference fields on the model(필터는 모델을 참조할 수 있습니다)

F표현식은 쿼리 내 모델 필드에 대한 참조 역할을 합니다. 참조를 사용하여 동일한 모델 instance에 있는 서로 다른 두 필드의 값을 비교할 수 있습니다.

예를 들어 pingbacks보다 댓글이 더 많은 모든 블로그 항목을 찾으려면 F()객체를 생성해서 pingback 수를 참조하고 해당 F() 객체를 쿼리에 사용합니다.

>>> from django.db.models import F
>>> Entry.objects.filter(number_of_comments__gt=F("number_of_pingbacks"))

Django는 F()에 대해 덧셈, 뺄셈, 곱셈, 나눗셈, 모듈로 및 거듭제곱 연산의 사용을 지원합니다.

>>> Entry.objects.filter(number_of_comments__gt=F("number_of_pingbacks") * 2)
>>> Entry.objects.filter(rating__lt=F("number_of_comments") + F("number_of_pingbacks"))

작성자 이름이 블로그 이름과 동일한 모든 항목을 검색하려면 다음 쿼리를 실행할 수 있습니다.

>>> Entry.objects.filter(authors__name=F("blog__name"))

날짜 및 날짜/시간 fields도 timedelta object를 더하거나 뺼 수 있습니다. 다음은 게시 후 3일 이상 수정된 모든 항목을 반환합니다.

>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F("pub_date") + timedelta(days=3))

그리고 비트 연산 또한 지원합니다.


F()와 함께 주석을 사용

F()는 다양한 필드와 산술과 결합하여 모델에 dynamic fields를 생성할 수 있습니다.

company = Company.objects.annotate(chairs_needed=F("num_employees") - F("num_chairs"))

결합하는 필드의 유형이 다른 경우 어떤 종류의 필드가 반환될지 Django에게 알려야합니다. 대부분의 표현식은 output_field를 지원하지만 F()를 지원하지 않으므로 ExpressionWrapper로 표현식을 래핑해야 합니다.

from django.db.models import DateTimeField, ExpressionWrapper, F

Ticket.objects.annotate(
    expires=ExpressionWrapper(
        F("active_at") + F("duration"), output_field=DateTimeField()
    )
)

FoeignKey와 같은 관계형 필드를 참조할 때 F()는 모델 인스턴스가 아닌 기본 키 값을 반환합니다.

>>> car = Company.objects.annotate(built_by=F("manufacturer"))[0]
>>> car.manufacturer
<Manufacturer: Toyota>
>>> car.built_by
3

F()를 이용해서 null 값 정렬

필드의 null 값 순서를 제어하려면 Expression.asc()또는 desc()에 F() 및 nulls_first 또는 nulls_last 키워드를 이용하세요

from django.db.models import F

Company.objects.order_by(F("last_contacted").desc(nulls_last=True))