Перейти к содержимому

Вычисляемые и Stored Computed Fields в Odoo: Полное руководство

Разберитесь, как в Odoo работают вычисляемые поля с сохранением значения: когда они нужны и как их создавать — через Python-код или с помощью Odoo Studio. Эта статья объяснит принципы хранения вычисленных результатов, подскажет сценарии, в которых имеет смысл сохранять значение (чтобы ускорить отчёты и сортировку, сохранять исторические данные или уменьшить нагрузку на вычисления), и пошагово покажет реализацию — от написания метода @api.depends и определения поля fields.Float/Char с параметром store=True до настройки триггеров и обновления при изменении зависимостей. Также рассмотрим альтернативы: когда лучше использовать не сохраняемые вычисляемые поля, функции-ассеты и автоматические действия в Studio, а когда — полноценное поле в базе. В конце — типичные ошибки (неправильные depends, слишком частые пересчёты, пропуск обновления при изменении связанных записей) и советы по тестированию, миграциям и производительности.
6 марта 2026 г. от
Вычисляемые и Stored Computed Fields в Odoo: Полное руководство
Dasolo
| Комментариев пока нет

Введение


Если вы работали с Odoo, наверняка встречали поля, значение которых формируется автоматически. Сохраняемое вычисляемое поле — это то же самое, но результат вычисления не просто выводится на экран, а записывается в базу данных и ведёт себя как обычный столбец таблицы.


Это различие важнее, чем кажется. Поле, которое пересчитывается «на лету», нельзя эффективно искать, фильтровать, группировать или экспортировать через SQL. Сохраняемое вычисляемое поле доступно для этих операций, потому что его значение хранится в БД и участвует в запросах как любой другой атрибут записи.


В этой статье разберём всё, что нужно знать о сохраняемых вычисляемых полях в Odoo: как они устроены в рамках модели данных, как их добавлять через Studio и через Python-модуль, где применяются в бизнес-процессах и какие ошибки чаще всего допускают при работе с ними.

Что такое сохраняемое вычисляемое поле в Odoo


В ORM Odoo каждое поле модели хранит данные. Большинство полей заполняются пользователем. Вычисляемое поле отличается тем, что его значение формируется функцией на Python, а не вводится вручную.


Сохраняемое вычисляемое поле — это вычисляемое поле с параметром store=True. Когда меняются указанные зависимости, Odoo вызывает функцию вычисления и записывает результат в колонку таблицы, после чего значение становится доступно как у обычного поля.


Типовой шаблон в Python выглядит так:

total_amount = fields.Float(
    string='Total Amount',
    compute='_compute_total_amount',
    store=True,
)

@api.depends('quantity', 'unit_price')
def _compute_total_amount(self):
    for record in self:
        record.total_amount = record.quantity * record.unit_price

Именно параметр store=True отличает сохраняемое вычисляемое поле от несохраняемого: без него значение пересчитывается при каждом чтении и не сохраняется в базе данных.


В интерфейсе Odoo такое поле ничем не выделяется: пользователи видят его на формах, в списках и отчётах, могут фильтровать и группировать по нему или включать в экспорт. С точки зрения конечного пользователя оно выглядит как обычное поле.


Сравнение: сохраняемое vs несохраняемое

Понимание разницы критично для разработки в Odoo:

  • Несохраняемое вычисляемое поле: значение вычисляется при каждом чтении. Нельзя использовать в поиске, группировке или фильтрах. Экономит место, но недоступно для SQL-запросов.
  • Сохраняемое вычисляемое поле: пересчитывается при изменении зависимостей и записывается в базу. Доступно для поиска, фильтрации и экспорта. Занимает место в БД, как обычный столбец.

Выбор между ними зависит не от того, какое лучше «вообще», а от задач. Если поле только отображается в одной форме — можно не сохранять. Если нужно фильтровать, сортировать или агрегировать — выбирайте store=True.

Как это поле работает


При определении сохраняемого поля ORM Odoo автоматически настраивает триггеры пересчёта на основе полей, перечисленных в декораторе @api.depends().


Когда одна из зависимостей меняется, Odoo помечает запись как требующую пересчёта, выполняет compute-функцию и записывает новое значение в колонку таблицы.


Жизненный цикл пересчёта

Пошагово это выглядит так:

  1. Пользователь или автоматический процесс меняет поле, указанное в @api.depends().
  2. Odoo обнаруживает изменение и находит все зависимые записи.
  3. Вызвается метод вычисления для этих записей.
  4. Вычисленное значение записывается в колонку базы данных.
  5. Поле становится доступно для поиска, фильтрации и экспорта с новым значением.

Обычно пересчёт происходит синхронно в той же транзакции. При массовых операциях Odoo может откладывать часть пересчётов и выполнять их в фоне, чтобы не блокировать основной процесс.


Зависимости между связанными моделями

@api.depends() поддерживает «точечные» пути к полям связанных моделей, например:


@api.depends('partner_id.country_id.name')
def _compute_country_name(self):
    for record in self:
        record.country_name = record.partner_id.country_id.name or ''

В этом случае Odoo отслеживает изменения partner_id, country_id и name в связанных моделях. Если название страны изменится, все зависящие записи автоматически пересчитаются — это один из ключевых мощных механизмов фреймворка Odoo.


Влияние на базу данных

Поскольку поле сохраняется, в PostgreSQL появляется реальная колонка. Это позволяет задействовать его в SQL-запросах напрямую — поиск и фильтрация по таким полям быстры и экономичны, как по обычным столбцам.

Бизнес-кейсы


Примеры из реальной практики


1) Продажи: процент маржи на строке заказа

Команде продаж удобно видеть маржу по каждой строке заказа. Сохраняемое поле берёт цену продажи и себестоимость, вычисляет маржу и сохраняет её. Менеджер может фильтровать строки по марже, быстро находить убыточные позиции и группировать отчёты по диапазонам маржи.


2) CRM: дни без активности по лидам

В CRM поле, которое хранит количество дней с последней активности, позволяет автоматически отбирать «забытые» лиды. Планировщик запускает пересчёт каждый день — менеджеры видят лиды без активности и могут оперативно реагировать.


3) Склад: доступное количество на складе

Для товаров с хитрыми складскими правилами поле может хранить разницу между наличием и резервами. Поскольку значение хранится, менеджеры могут сразу сортировать или фильтровать товары по доступности без тяжёлых расчётов в реальном времени.


4) Бухучёт: количество просроченных счетов у клиента

На карточке контрагента сохраняемое поле может считать количество просроченных счетов. Бухгалтеры сразу сортируют контакты по числу просроченных платежей — это возможно только, когда значение записано в БД.


5) Производство: суммарная оценка времени работ

В спецификации изделия (BOM) поле может суммировать длительности всех операций. Планировщики сортируют и фильтруют BOM по общей длительности для планирования мощностей — при изменении операций сумма обновляется автоматически.

Создание и кастомизация поля


Способы создания сохраняемых вычисляемых полей


Есть два пути: быстро — через Odoo Studio для простых случаев, и полноценно — через Python-модуль, когда нужна гибкость.

Через Odoo Studio


Studio позволяет добавить вычисляемое поле без кода: для числовых типов можно задать формулу, похожую на Python-выражение, и Studio само настроит отслеживание зависимостей. Это удобно для простых арифметических выражений, но имеет ограничения — сложно работать с полями связанных моделей, условной логикой или агрегацией по дочерним записям.


Studio хорош для быстрых правок и MVP, но при усложнении логики придётся перейти к разработке модуля на Python.


Через кастомный Python-модуль

Когда нужна сложная логика, поле определяется в Python в составе модуля. Пример — добавление процента маржи в sale.order.line:


from odoo import models, fields, api

class SaleOrderLine(models.Model):
    _inherit = 'sale.order.line'

    x_margin_pct = fields.Float(
        string='Margin %',
        compute='_compute_margin_pct',
        store=True,
        digits=(5, 2),
    )

    @api.depends('price_unit', 'purchase_price')
    def _compute_margin_pct(self):
        for line in self:
            if line.price_unit:
                line.x_margin_pct = (
                    (line.price_unit - line.purchase_price) / line.price_unit
                ) * 100
            else:
                line.x_margin_pct = 0.0

После установки модуля Odoo создаёт колонку x_margin_pct в базе, запустит пересчёт для существующих записей и начнёт отслеживать изменения указанных зависимостей.


Префикс x_ — устойчивая конвенция для пользовательских полей в Odoo, чтобы избежать конфликтов с полями ядра.


Сделать сохраняемое вычисляемое поле редактируемым

По умолчанию вычисляемые поля только для чтения. Если нужно, чтобы пользователь мог вручную менять значение, добавьте inverse-метод, который будет применять изменения к исходным полям. Это удобно, когда вычисление даёт разумное значение по умолчанию, но иногда требуется ручная корректировка.


Studio-поля и XML-RPC

Через XML-RPC можно создавать записи в ir.model.fields для стандартных полей, но если требуется вычисляемая логика на Python, код должен жить на сервере в модуле. API удобен для автоматического развёртывания простых полей, но сам compute-метод всегда должен быть в серверном модуле.

Рекомендации по использованию


Практики, которые применяют опытные консультанты Odoo


Точно указывать все зависимости

Декоратор @api.depends должен перечислять каждое поле, которое читается в compute-методе. Пропуск даже одного поля приведёт к тому, что значение не будет обновляться при изменении этой зависимости. Проверьте код построчно и добавьте все пути к полям.


Делать методы вычисления быстрыми

Compute-методы выполняются для всех затронутых записей — в загруженной системе это могут быть тысячи строк. Минимизируйте дополнительные запросы к базе внутри метода. Если нужно данные связанных записей, используйте уже загруженные поля вместо лишних search/ browse-запросов.


Использовать store=True только при необходимости

Сохраняемые поля занимают место и генерируют записи в БД при каждом пересчёте. Если поле нужно только для отображения и никогда не будет использовано в фильтрах или группировках, лучше ограничиться несохраняемым вычисляемым полем.


Обрабатывать крайние случаи внутри метода

Проверяйте пустые значения, деление на ноль, отсутствие связанных записей и другие аномалии — эти ситуации часто становятся причиной скрытых ошибок. Задавайте безопасные значения по умолчанию, если вычисление не может быть выполнено.


Планировать первичный пересчёт на больших таблицах

При установке модуля Odoo пересчитывает поле для всех существующих записей. На таблице с сотнями тысяч строк это может занять время. Тестируйте миграцию в стенде и готовьте план: фоновые задачи или окно обслуживания для продакшена.


Избегать циклических зависимостей

Если поле A зависит от B, а B зависит от A, при загрузке модуля возникнет ошибка. Стройте поток зависимостей в одну сторону.

Распространённые ошибки


Частые ошибки — забыть указать store=True

Это самая распространённая оплошность. Поле выглядит на форме, всё кажется рабочим, но при попытке отфильтровать или включить поле в отчёт выясняется, что оно не доступно. Прежде чем писать логику, решите, нужно ли поле для поиска — если да, укажите store=True сразу.


Пропуск зависимости в @api.depends

Если метод читает partner_id.country_id, а в декораторе указан только partner_id, изменение страны не вызовет пересчёт. Пройдите по каждому пути доступа и перечислите все шаги в декораторе.


Скрытые ошибки в compute-методе

Если метод бросает исключение для конкретной записи, Odoo пропускает её пересчёт и оставляет старое значение. Ошибки могут появиться в логах, но не для пользователя. Тестируйте вычисления на данных с пропусками и нетипичными случаями.


Падение производительности на больших объёмах данных

Метод, который нормально работает в разработке, может стать узким местом в продакшене при росте таблицы до десятков тысяч записей. Следите за количеством запросов на запись — лишний запрос на запись, умноженный на 10 000 записей, даст 10 000 лишних запросов.


Использование sudo() внутри compute-методов

Вызов sudo() для обхода прав доступа — риск с точки зрения безопасности. Если вычисляемое поле раскрывает данные, которые пользователь не должен видеть, вы нарушите модель доступа. Применяйте sudo() только после осознанной проверки последствий.


Ожидание немедленного пересчёта во всех сценариях

В интерактивных операциях пересчёт обычно синхронный, но при массовом импорте, фоновых задачах или в особых контекстах Odoo может отложить пересчёт. Не полагайтесь на то, что значение всегда актуально моментально после записи — проверьте поведение в тех сценариях, где поле используется.

Заключение


Итог


Сохраняемое вычисляемое поле — один из самых мощных инструментов при расширении Odoo. Оно автоматизирует расчёты, помогает держать данные в согласованном виде и делает значения доступными для поиска и отчётности без ручного труда.

  • Главные выводы:
  • Используйте store=True, когда поле должно быть доступно для поиска, фильтрации или экспорта.
  • Всегда указывайте все зависимости в @api.depends(), включая пути по связанным моделям.
  • Держите методы вычисления быстрыми и обязательно обрабатывайте крайние случаи.
  • Для простых формул подойдёт Odoo Studio, для сложной логики — Python-модуль. Планируйте первичный пересчёт при развёртывании на больших таблицах.

Независимо от того, создаёте ли вы новый модуль, расширяете модель или изучаете типы полей в Odoo, понимание сохраняемых вычисляемых полей важно: они лежат на границе ORM, базы данных и бизнес-логики.


Нужна помощь с внедрением Odoo?

Dasolo помогает компаниям внедрять, настраивать и оптимизировать Odoo под разные бизнес-задачи. Если нужно добавить вычисляемые поля, собрать отчёты на основе вычисляемых значений или развивать собственную разработку Odoo — наша команда готова помочь.


Свяжитесь с нами для обсуждения вашего кейса — мы подберём подходящее решение и поможем с реализацией.

Вычисляемые и Stored Computed Fields в Odoo: Полное руководство
Dasolo 6 марта 2026 г.
Поделиться этой записью
Войти оставить комментарий