Таблица обновления Django с использованием данных из другой таблицы

У меня есть 2 таблицы products и catagories, связанные внешним ключом. Мне нужно обновить поле products.new_cost с помощью поля catagories.price_markup следующим образом:

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

В SQL это так просто, но как это сделать с помощью Django ORM?

Моя упрощенная попытка не работает 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'))

Ответ 1

ОБНОВЛЕНИЕ: Кажется, это или не работает или устарело. Учитывая, что я не использовал Django около 6 лет, я рекомендую взглянуть на другие ответы в этом вопросе вместо этого. Я бы хотел, чтобы этот пункт был помечен и удален, если это возможно, поскольку я вполне уверен, что в настоящее время он вводит в заблуждение и больше не действителен (учитывая оценку -10).

Django использует __ (двойное подчеркивание) для связанных полей. Измените этот category.price_markup на category__price_markup и вы должны быть в открытом виде.

Ответ 2

Вы не можете использовать F, но вы можете использовать Subquery и OuterRef:

from django.db.models import Subquery, OuterRef

cost = Category.objects.filter(
    id=OuterRef('category_id')
).values_list(
    'price_markup'
)[:1]

Product.objects.update(
    new_cost=Subquery(cost)
)

Ответ 3

Примечание: мой ответ устарел, Django 1.11 представил OuterRef, который реализует эту функцию. Проверьте ответ андрея Беренды.

Согласно документации, обновления с использованием предложений объединения не поддерживаются, см.:

Однако, в отличие от объектов F() в фильтрах и исключениях, вы не можете вводить объединения при использовании объектов F() в обновлении - вы можете только опорные поля, локальные для обновляемой модели. Если вы попытаетесь представьте соединение с объектом F(), будет вызвано FieldError:

# THIS WILL RAISE A FieldError
>>> Entry.objects.update(headline=F('blog__name'))

Кроме того, в соответствии с этой проблемой, это сделано специально, и в ближайшем будущем не планируется ее менять:

Фактическая проблема здесь заключается в том, что объединенные предложения F() не разрешено в выражениях update(). Это по замыслу; поддержка объединений в update() были явно удалены из-за осложнения в их поддержке в общем случае.

Ответ 4

AFAIU может быть обмануто с помощью

for row in ModelName.objects.filter(old_field__isnull=False):
     row.new_field = row.old_field.subfield
     row.save()

Ответ 5

from django.db import transaction

with transaction.atomic():
    for p in Product.objects.select_related('category').filter(update=1)
        p.new_cost= p.pleer_cost * p.category.price_markup
        p.save()