PageView

Django Tips #13 Using F() Expressions

Django Tips #13 Using F() Expressions

In the Django QuerySet API, F() expressions are used to refer to model field values directly in the database. Let’s say you have a Product class with a price field, and you want to increase the price of all products in 20%.

A possible solution would be:

products = Product.objects.all()
for product in products:
    product.price *= 1.2
    product.save()

Instead you could use an F() expression to update it in a single query:

from django.db.models import F

Product.objects.update(price=F('price') * 1.2)

You can also do it for a single object:

product = Product.objects.get(pk=5009)
product.price = F('price') * 1.2
product.save()

But take care with this kind of assignment. The F() object persist after saving the model.

product.price                   # price = Decimal('10.00')
product.price = F('price') + 1
product.save()                  # price = Decimal('11.00')
product.name = 'What the F()'
product.save()                  # price = Decimal('12.00')

So, basically after updating a field like that, product.price will hold an instance of django.db.models.expressions.CombinedExpression, instead of the actual result. If you want to access the result immediately:

product.price = F('price') + 1
product.save()
print(product.price)            # <CombinedExpression: F(price) + Value(1)>
product.refresh_from_db()
print(product.price)            # Decimal('13.00')

You can also use it to annotate data:

from django.db.models import ExpressionWrapper, DecimalField

Product.objects.all().annotate(
    value_in_stock=ExpressionWrapper(
        F('price') * F('stock'), output_field=DecimalField()
    )
)

Since price is a DecimalField and stock is a IntegerField, we need to wrap the expression inside a ExpressionWrapper object.

It can be used to filter data as well:

Product.objects.filter(stock__gte=F('ordered'))