Using a domain stored in a field for a Many2one
Tags: odoo, oca
Published: 2022-10-15 18:00:47

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:

```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:

```xml
<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).

![Image of browser freezing with "a web page is slowing down your browser" message](https://dev-to-uploads.s3.amazonaws.com/i/yz8sws3v26r7jn8f5jpj.png)
 
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](https://github.com/OCA/web/tree/13.0/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:

```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:

```xml
<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`](https://github.com/OCA/wms/pull/12/commits/ef111b25be41cbcd00c8f404fa3220f8b0900178).