It is a very common pattern in Odoo to filter available records in a Many2one depending on another field.
For instance, users can select a Stock Location only if it abides by some rules. You may have a computed field, that gives the allowed locations for a move line.
The issue
Let's take a fictive use case where we allow moving a product in a location only if this location is associated to this product.
In Python:
class StockMoveLine(models.Model):
_inherit = "stock.move.line"
allowed_location_ids = fields.Many2many(
comodel_name="stock.location",
compute="_compute_allowed_location_ids"
)
@api.depends("product_id", "move_id.location_dest_id")
def _compute_allowed_location_ids(self):
for line in self:
destination = line.move_id.location_dest_id
line.allowed_location_ids = destination.allowed_sublocations(line.product_id)
And in the XML view:
<field name="allowed_location_ids" invisible="1" />
<field name="location_dest_id" domain="[('id', 'in', allowed_location_ids)]"/>
It works quite well, until you have a large number of locations, e.g. 5000 allowed locations. When you change the product, an onchange
is triggered to get the new allowed_location_ids
, which is done very fast server_side, but the front-end starts to freeze (5-10 seconds).
This issue happens because we return 5000 ids to a Many2many widget, which has to process every single id.
ℹ️ I encountered this issue in Odoo 13.0, it may or may not still happen on newer versions.
The solution
Here comes to the rescue the OCA module web_domain_field.
As stated in its description:
In order to mitigate these limitations this new addon allows you to use the value of a field as domain of another field in the xml definition of your view.
Go read the description of the module as it gives other interesting use cases.
Using a domain set directly in the field short-circuits the Many2many widget on the view and fixes the performance issue.
Our code would now look like:
In Python:
class StockMoveLine(models.Model):
_inherit = "stock.move.line"
allowed_location_domain = fields.Char(
compute="_compute_allowed_location_domain"
)
@api.depends("product_id", "move_id.location_dest_id")
def _compute_allowed_location_domain(self):
for line in self:
destination = line.move_id.location_dest_id
allowed_locations = destination.allowed_sublocations(line.product_id)
line.allowed_location_domain = json.dumps([("id", "in", allowed_locations.ids)])
And in the XML view:
<field name="allowed_location_domain" invisible="1" />
<field name="location_dest_id" domain="allowed_location_domain"/>
The real use case used to showcase this Odoo module was in stock_storage_type
.
Top comments (2)
Great addon! Solved us a lot of performance issues.
The domain field shouldn't be fields.Char?
Oh, yes of course! Thanks for the correction :)