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
.
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.
In order to compute the stock levels in a given location for all the products, you need to perform the following steps:
product.product
:class ProductProduct(models.Model): _inherit = 'product.product'
stock_in_location()
:@api.model def stock_in_location(self, location):
product.product
recordset with a context modified as follows:product_in_loc = self.with_context( location=location.id, active_test=False )
all_products = product_in_loc.search([])
stock_levels = [] for product in all_products: if product.qty_available: stock_levels.append((product.name, product.qty_available)) return stock_levels
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.
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.
3.145.46.18