Блог

Как использовать группировку по полям типа "многие ко многим" в Odoo 19

post-title

ORM в Odoo — мощный инструмент, но разработчики часто сталкиваются с трудностями при группировке записей по полю типа Many2Many. Поля M2M работают иначе, чем обычные поля базы данных: значения хранятся не непосредственно в основной таблице, а в реляционной таблице. Из-за этого Odoo не может применять к ним свою обычную логику « GROUP BY », что приводит к ошибкам утверждения при попытке группировки непосредственно по полю M2M.

Надежным обходным путем является создание вычисляемого, хранимого поля, представляющего значения M2M в простом формате (обычно это строка, разделенная запятыми). После того, как это поле появится в базе данных , оно станет доступным для группировки в представлениях поиска.

Ниже приведён практический пример, основанный на группировке заказов на продажу по тегам товаров, входящих в их состав.

Пошаговая реализация

Мы расширим модель sale.order , чтобы группировать заказы на продажу по тегам товаров из строк заказа. Предположим, у нас есть пользовательский модуль с именем sale_tag_grouping.

Шаг 1: Определение вычисляемого поля (Python)

В вашем пользовательском модуле (например, sale_tag_grouping ) расширьте модель sale.order и вычислите поле на основе тегов товаров.

from odoo import models, fields, api
class SaleOrder(models.Model):
    _inherit = 'sale.order'
    product_tags = fields.Char(
        string='Product Tags',
        compute='_compute_product_tags',
        store=True
    )
    @api.depends('order_line.product_id.product_tag_ids.name')
    def _compute_product_tags(self):
        for order in self:
            tags = set()
            for line in order.order_line:
                tags.update(line.product_id.product_tag_ids.mapped('name'))
            order.product_tags = ', '.join(sorted(tags)) if tags else ''

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

  • product_tag_ids — это поле M2M в описании товара.
  • Функция `set()` в Python гарантирует отсутствие дубликатов.
  • Сортировка имен обеспечивает согласованность выходных данных (полезно для группировки).
  • Пустая строка позволяет избежать нулевых значений и обеспечивает аккуратность фильтров пользовательского интерфейса.

Поскольку поле хранится в памяти , оно становится настоящим столбцом базы данных, что является ключевым требованием для корректной работы функции группировки (Group By).

Шаг 2: Обновите представление поиска (XML)

В файле представлений (например, views/sale_order_views.xml ) унаследуйте базовое представление поиска и добавьте фильтр для группировки.

Далее расширьте область поиска по заказам на продажу.

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <record id="view_sales_order_filter_inherit" model="ir.ui.view">
        <field name="name">sale.order.search.inherit.tag.grouping</field>
        <field name="model">sale.order</field>
        <field name="inherit_id" ref="sale.view_sales_order_filter"/>
        <field name="arch" type="xml">
            <xpath expr="//search" position="inside">
                <separator/>
                <filter name="product_tags"
                        string="Group by Product Tags"
                        domain="[]"
                        context="{'group_by': 'product_tags'}"/>
                <separator/>
            </xpath>
        </field>
    </record>
</odoo>
  • Объяснение :
    • context="{'group_by': 'product_tags'}" указывает Odoo на необходимость группировки по вычисляемому полю.
    • Пустой домен означает, что дополнительная фильтрация не применяется.

После загрузки раздел « Теги товаров » появится в панели поиска заказов в разделе « Группировка по» > «Пользовательская группа».

После определения XML-представления перейдите в пользовательский интерфейс Odoo и обновите модуль, чтобы применить изменения. Это добавит поле tag_names в форму и включит группировку по тегам.

Шаг 3: (Необязательно) Отобразите поле в форме/древовидном представлении.

Если вы хотите, чтобы пользователи видели вычисленные теги напрямую:

<record id="view_order_form_inherit" model="ir.ui.view">
    <field name="name">sale.order.form.inherit.tag.grouping</field>
    <field name="model">sale.order</field>
    <field name="inherit_id" ref="sale.view_order_form"/>
    <field name="arch" type="xml">
        <xpath expr="//field[@name='partner_id']" position="after">
            <field name="product_tags" readonly="1"/>
        </xpath>
    </field>
</record>

Шаг 4 — Обновите модуль и протестируйте.

  1. Добавьте импорты в файл __init__.py.
  2. Добавьте ваши XML-файлы в файл __manifest__.py.
  3. Обновите модуль через меню «Приложения».
  4. Откройте раздел «Продажи» > «Заказы» .
  5. Используйте Поиск > Группировка по > Теги товаров.

Теперь вы должны увидеть заказы, сгруппированные на основе объединенных товарных тегов, найденных в строках заказа.

В Odoo невозможно группировать данные по полям M2M напрямую, поскольку они не хранятся в таблице модели. Создав хранимое вычисляемое поле, представляющее содержимое M2M, вы можете использовать встроенные механизмы группировки Odoo без проблем с производительностью. Этот подход универсален — его можно применять к тегам, категориям или пользователям в различных модулях, таких как CRM, управление запасами или управление персоналом.