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))
'Django > orm' 카테고리의 다른 글
DynamoDB() (0) | 2024.02.05 |
---|---|
Django date range (0) | 2024.02.01 |
len() vs count() (0) | 2024.01.19 |
두 가지 조건을 exclude 시 and 연산 (0) | 2024.01.17 |
검색 기능 splite기능 annotate만들어서 사용 (2) | 2024.01.02 |