I have 2 tables products
and catagories
connected by foreign key.
I need to update field products.new_cost
using field catagories.price_markup
as following:
UPDATE products p
INNER JOIN categories c ON p.category_id = c.id
SET p.new_cost = ROUND(p.pleer_cost * (1 + c.price_markup/100), -1)
WHERE p.update = 1
In SQL it's so easy, but how to do it using Django ORM?
My simplified try doesn't work Cannot resolve keyword 'category.price_markup' into field.
:
Product.actived.select_related('category').filter(update=1)).update(new_cost=F('pleer_cost') * F('category.price_markup'))
Django uses __
(double underscore) for related fields. Change that category.price_markup
to a category__price_markup
and you should be in the clear.
Note: My answer is outdated now, Django 1.11 introduced OuterRef
which implements this feature. Check Andrey Berenda answer below.
According to the documentation, updates using join clauses are not supported, see:
However, unlike F() objects in filter and exclude clauses, you can’t
introduce joins when you use F() objects in an update – you can only
reference fields local to the model being updated. If you attempt to
introduce a join with an F() object, a FieldError will be raised:
# THIS WILL RAISE A FieldError
>>> Entry.objects.update(headline=F('blog__name'))
Also, according to this issue, this is by design and there is no plans to change it in the near future:
The actual issue here appears to be that joined F() clauses aren't
permitted in update() statements. This is by design; support for joins
in update() clauses was explicitly removed due to inherent
complications in supporting them in the general case.
You cannot use F, but you can use Subquery and OuterRef:
from django.db.models import Subquery, OuterRef
cost = Category.objects.filter(
id=OuterRef('product_id')
).values_list(
'price_markup'
)[:1]
Product.objects.update(
new_cost=Subquery(cost)
)
AFAIU it can be workarounded with
for row in ModelName.objects.filter(old_field__isnull=False):
row.new_field = row.old_field.subfield
row.save()