F() 표현식으로 django 쿼리 업그레이드
TL; DR
읽기/쓰기 작업을 위해 모델 필드를 참조할 때 F() 표현식을 사용합시다.
대량 업데이트
귀하의 국가에서 정부가 세율을 5% 인상하여 귀하가 리스팅 제품 가격을 20% 인상해야 한다고 가정합니다. django 쿼리는 어떻게 생겼습니까?
class Product(models.Model):
name = models.TextField()
price = models.DecimalField()
in_stock = models.IntegerField(
help_text="Number of items available in inventory"
)
여러 제품을 업데이트하는 순진한 구현은 다음과 같을 수 있습니다.
products = Product.objects.all()
for product in products:
product.price *= 1.2
product.save()
이 경우 각 레코드를
SELECT price FROM product
다음에 UPDATE product SET price = new_value WHERE condition
수행합니다. 각 개체에 대해 2개의 쿼리(READ용 1개, WRITE용 1개)를 의미합니다.좀 더 신중하게 생각해보면 새로운 가격은 그것이 무엇이든 현재 가격에 상대적이라는 것을 알 수 있습니다. 직관적으로 업데이트 프로세스를 실행할 때
price
모델의 Product
필드를 참조하려고 합니다.이제 F() 식입니다. The Django official doc 상태:
An F() object represents the value of a model field, transformed value of a model field, or annotated column.
It makes it possible to refer to model field values and perform database operations using them without actually having to pull them out of the database into Python memory.
F()
및 update()
queryset 메서드로 문제를 해결해 봅시다.from django.db.models import F
Product.objects.update(price=F("price")*1.2)
위의 쿼리는 인스턴스 속성에 값을 할당하는 일반적인 Python처럼 보이지만 실제로는 SQL 표현식입니다. 이 식은 데이터베이스에 데이터베이스의 price 필드에 120%를 곱하도록 지시합니다.
새 가격 값은 현재 가격 값을 기반으로 하므로 Python 메모리에 로드할 필요가 없습니다. 이것이 F()가 작동하는 이유입니다.
단일 개체 업데이트
모든 주문 결제가 완료된 후
in_stock
필드를 업데이트한다고 가정해 보겠습니다.순진한 구현은 다음과 같을 수 있습니다.
def process_payment(product: Product):
with transaction.atomic():
payment = Payment.objects.create(product=product)
product.in_stock = product.in_stock - 1
product.save(update_fields=["in_stock"])
그래서 문제가 무엇입니까?
제품 주문을 시도하는 여러 사용자가 있다고 가정해 보겠습니다. 시나리오는 다음과 같습니다.
공정 1
프로세스 2
재고
선택
in_stock
-> 55
선택
in_stock
-> 55
업데이트
in_stock
= 5-14
업데이트
in_stock
= 5-14
이 경우 두 프로세스
product.in_stock
가 동시에 업데이트되지만in_stock
값이 1씩 감소합니다. 이는 잘못된 것입니다.주요 문제는 가져온 항목을 기반으로 감소
in_stock
한다는 것입니다. 데이터베이스에 현재 저장된 항목을 기반으로 업데이트 명령in_stock
을 제공하면 어떻게 됩니까?def process_payment(product: Product):
with transaction.atomic():
payment = Payment.objects.create(product=product)
product.in_stock = F("in_stock") - 1
product.save(update_fields=["in_stock"]])
두 접근 방식의 차이는 매우 적지만 업데이트 명령으로 생성된 SQL을 살펴보겠습니다.
순진한 접근 방식:
UPDATE product_product
SET in_stock = 4
WHERE id = 262;
이렇게 하면 데이터베이스의 in_stock 현재 값에 관계없이 수량 = 4가 감소합니다.
F() 접근법:
UPDATE product_product
SET in_stock = in_stock + 1
WHERE id = 262;
ID가 262인 제품의 수량은 1개 감소하며 고정된 값으로 설정되지 않습니다. 경쟁 조건 문제를 해결하기 위해 F 표현식을 사용하는 방법입니다.
메모
모델 필드에 할당된 F() 개체는 모델 인스턴스를 저장한 후에도 지속되며 각각에 적용되므로
save()
업데이트된 인스턴스를 가져오려면refresh_from_db
필요합니다.데이터베이스에서 새로 고치지 않고 인스턴스를 읽으려고 하면 예기치 않은 결과가 발생할 수 있습니다.
In [12]: product = Product.objects.get(id=262)
In [13]: product.in_stock = F("in_stock") - 1
In [14]: product.save()
In [15]: product.in_stock
Out[15]: <CombinedExpression: F(in_stock) - Value(1)>
In [16]:
요약
기사 전체에서 우리는 F() 표현식의 두 가지 사용 사례를 지적했습니다.
Reference
이 문제에 관하여(F() 표현식으로 django 쿼리 업그레이드), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/hoangquochung1110/upgrade-django-queries-with-f-expression-5025텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)