Call a method with a modified context

The context is part of the environment of a recordset. It is used to pass information such as the timezone or the language of the user from the user interface as well as contextual parameters specified in actions. A number of methods in the standard addons use the context to adapt their behavior to these values. It is sometimes necessary to modify the context on a recordset to get the desired results from a method call or the desired value for a computed field.

This recipe shows how to read the stock level for all product.product models in a given stock.location.

Getting ready

This recipe uses the stock and product addons. For our purposes, here is a simplified version of the product.product model:

class product.product(models.Model):
    _name = 'product.product'
    name = fields.Char('Name', required=True)
    qty_available = fields.Float('Quantity on Hand',
                                 compute='_product_available')
    def _product_available(self):
        """if context contains a key 'location' linked to a
        database id, then the stock available is computed within
        that location only. Otherwise the stock of all internal
        locations is computed"""
        pass  # read the real source in addons/stock/product.py :)

We intentionally don't provide the implementation of the computation and we skipped a few other keys that are looked for in the context in order to focus on the recipe.

How to do it…

In order to compute the stock levels in a given location for all the products, you need to perform the following steps:

  1. Create a model class extending product.product:
    class ProductProduct(models.Model):
        _inherit = 'product.product'
  2. Add a method called stock_in_location():
        @api.model
        def stock_in_location(self, location):
  3. In the method, get a product.product recordset with a context modified as follows:
            product_in_loc = self.with_context(
                location=location.id,
                active_test=False
            )
  4. Search all products:
            all_products = product_in_loc.search([])
  5. Create an array with the product name and stock level of all products present in the specified location:
            stock_levels = []
            for product in all_products:
                if product.qty_available:
                    stock_levels.append((product.name,
                                         product.qty_available))
            return stock_levels

How it works…

Step 3 calls self.with_context() with some keyword arguments. This returns a new version of self (which is a product.product recordset) with the keys added to the current context. We are adding two keys:

  • location: This one is mentioned in the docstring of the product.product method computing the qty_available field.
  • active_test: When this key is present and linked to the False value, the search() method does not automatically add ('active', '=', True) to the search domain. Using this ensures that in step 4, we get all products, including the disabled ones.

When we read the value of product.qty_available in step 5, the computation of that field is made using only the specified stock location.

There's more…

It is also possible to pass a dictionary to self.with_context(), in which case the dictionary is used as the new context, overwriting the current one. So step 3 could also have been written like this:

    new_context = self.env.context.copy()
    new_context.update({'location': location.id,
                        'active_test': False})
    product_in_loc = self.with_context(new_context)

Using with_context() involves creating a new Environment instance. This environment will have an initially empty recordset cache, and that cache will evolve independently of the cache of self.env. This can cause spurious database queries. In any case, you should avoid creating new environments inside loops and try to move these environment creations to the outmost possible scope.

See also

  • The Obtain an empty recordset for a model recipe in Chapter 5, Basic Server Side Development, explains what the environment is
  • The Passing parameters to forms and actions: Context recipe in Chapter 8, Backend Views, explains how to modify the context in action definitions
  • The Search for records recipe in Chapter 5, Basic Server Side Development, explains active records
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.46.18