Many2many fields are similar to Many2one (they create a new relationship between models), except that multiple records can be selected using many2many records and you cannot group by many2many fields in Odoo. When you provide a group by filter using a many2many field, you will get an error message like Assertion error.
This blog will provide insight on group_by for Many2many fields.
The following error message is depicted when a group_by is allocated to many2many fields “AssertionError: Fields in 'group by' must be regular database-persisted fields (no function or related fields), or function fields with store=True" just as depicted in the following image.
Here I’m using the existing tag_ids many2many field for giving group_by filters. The code is shown below:
.py
tag_ids = fields.Many2many('crm.tag', 'sale_order_tag_rel', 'order_id', 'tag_id', string='Tags')
.xml
<record id="view_sales_order_filter_mtp_default_groupby" model="ir.ui.view">
<field name="name">sale.order.list.select.mtp.defaults</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/group/filter[@name='customer']" position="after">
<separator/>
<filter name="tag_ids" string="Tags" domain="[]" context="{'group_by': 'tag_ids'}"/>
<separator/>
</xpath>
</field>
</record>
But it is possible to do such a group_by filter using the Many2many field. It is possible with the computing field but if the value is two in many2many fields then it will be shown by comma (,) like tag1, tag2 .
.py
tag_id_custom = fields.Char(string='Tags', compute='_get_tags', store=True)
@api.model
@api.depends('tag_ids')
def _get_tags(self):
for rec in self:
if rec.tag_ids:
tag_custom = ','.join([p.name for p in rec.tag_ids])
else:
tag_id_custom = ''
rec.tag_id_custom = tag_custom
.xml
<record id="sale_view_form_custom" model="ir.ui.view">
<field name="name">sale.view.custom</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='payment_term_id']" position="after">
<field name = tag_id_custom/>
</xpath>
</field>
</record>
<record id="view_sales_order_filter_mtp_default_groupby" model="ir.ui.view">
<field name="name">sale.order.list.select.mtp.defaults</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/group/filter[@name='customer']" position="after">
<separator/>
<filter name="tag_id" string="Tags" domain="[]" context="{'group_by': tag_id_custom}"/>
<separator/>
</xpath>
</field>
</record>
Here we added a Char field tag_id_custom that is a computed field, which joins the tag name into that field. If there is more than one tag present in the tag_ids field then it will separate by comma(,). As the above screenshot, you can see the Product and Service tag join into the new Char field as Product, Service.
You can see there is a group by filter named Tags that we gave in the XML file. When we try to group by these sales orders it will be shown below.
Filter for Many2many field
To compute the value from many2many fields you can create an alternative Many2many field. I’ll give you an example for this. You must provide store=True for the field.
.py
newfield_id = fields.Many2one('example_ids', compute=_compute_newfield_id, store=True)
@api.depends('example_ids')
def _compute_newfield_id(self):
for record in self:
record.newfield_id = record.example_ids and record.example_ids[0] or False
.xml
<field name="newfield_id" string="Example" filter_domain="[(newfield_id,'ilike', self)]"/>
It is possible to search only among stored fields therefore, you can provide the domain as your needs. You can include the xpath for the location you want.