The Odoo 8 API introduced some novel concepts that provide a more object-oriented approach, significantly decreasing code repetition.
While these changes were definitely for the best, they created a major disparity between the two versions, and invalidated most of the code/tutorials that are available on the internet.
This article aims to highlight these changes, as well as provide some high level identification and conversion tactics.
—-
[[[TOC]]]
—-
= Model =
The old API went through a lot of stages, but all of the old models are in some way contained within the `osv` module.
Columns were defined using a `_columns` dictionary as a class attribute, and utilize lowercase method names for the fields.
Old API Model Example:
{{{ lang=python
from openerp.osv import osv, fields
class my_model(osv.osv):
_name = ‘my.model’
_columns = {
‘my_column’: fields.char(string=’My Column’)
}
}}}
New models are much cleaner, and have the fields as directly defined class attributes.
You also don’t need to define a string attribute if a humanized, title case form of the column name fits the purpose (`my_column` becomes `My Column`).
Odoo 8 API Model Example:
{{{ lang=python
from openerp import models, fields
class MyModel(models.Model):
_name = ‘my.model’
my_column = fields.Char()
}}}
== Inheritance ==
There are multiple inheritance mechanisms available for models.
You can use all of the mechanisms at once, if you so choose.
[[https://www.odoo.com/forum/help-1/question/the-different-openerp-model-inheritance-mechanisms-whats-the-difference-between-them-and-when-should-they-be-used-46#answer-190|Check out this article for more information on inheritance mechanisms.]]
Model Inheritance Types:
{{{ lang=python
class MyModel(models.Model):
_inherit = ‘my.model’ #< Direct heritage
_inherit = ['my.model', 'my.other.model'] #< Direct heritage
_inherits = {'my.model': 'field_name'} #< Polymorphic heritage
}}}
If you define the `name` attribute on an inherited model, you are creating a copy of the inherited model as a new model (and database table):
{{{ lang=python
class MyModelNew(models.Model):
_inherit = 'a.model'
_name = 'my.model.new'
new_field = fields.Char() #< This field isn't on the original model
}}}
== Recordset ==
All instances of Model are also instances of a Recordset.
A Recordset represents a sorted set of records of the same Model.
=== Pool vs. Env ===
In the old API you would get the model from the pool, then perform an action on it:
{{{ lang=python
partner_ids = self.pool('res.partner').search(cr, uid, [], context)
}}}
With the concept of an environment being introduced, you will now use that environment to get the model.
By using the environment, we are passing along the `cr`, `uid`, and `context` without having to send them through to the method as arguments:
{{{ lang=python
partner_ids = self.env['res.partner'].search([])
}}}
=== Old API Note ===
The Old API always returned IDs of Recordsets, which then needed to be browsed on to actually utilize:
{{{ lang=python
>> partner_ids = self.pool(‘res.partner’).search(cr, uid, [])
>> print partner_ids
[1, 2, 3] #< partner_ids is an Array representing the Primary Keys of the Result
>> partner_ids = self.pool(‘res.partner’).browse(cr, uid, partner_ids)
>> print partner_ids
res.partner(1, 2, 3) #< partner_ids is now a Recordset of res.partners
}}}
The Odoo 8 API directly returns Recordsets, which is much more useful:
{{{ lang=python
>> partner_ids = self.env[‘res.partner’].search([])
>> print partner_ids
res.partner(1, 2, 3) #< partner_ids is a Recordset of res.partners of the Result
}}}
One gotcha, however, is that you cannot write a Recordset directly to the database or use it in search domains.
You instead need to generate a list of IDs representing the Primary Keys of the Recordset.
This is likely for compatibility, and I assume will get changed in the future (don't quote me).
Odoo 8 API Domains:
{{{ lang=python
partner_id = self.env['res.partner'].browse(1)
user_id = self.env['res.users'].browse(1)
# Correct write on `user_id.partner_id`
user_id.write({
'partner_id': partner_id.id,
})
# Incorrect write on `user_id.partner_id`
user_id.write({
'partner_id': partner_id,
})
## The above fails because it is trying to directly write
## a Recordset to the DB
}}}
= Environment =
In the Odoo 8 API, the concept of an Environment is introduced. Its main objective is to provide an encapsulation around the cursor, user_id, and context in relation to the Recordsets and Caches:
[[image:Odoo-Environment-High-Level.png|link=source]]
This concept greatly improved the Odoo design by providing a more Object Oriented approach.
== Get the Context ==
When using the old API, you could obtain the environment context with the method arguments.
In the Odoo 8 API, you can obtain the context via the Environment:
{{{ lang=python
context = self._context
# OR
context = self.env.context
}}}
== Modify the Environment ==
The old method of changing the environment was to just pass an updated context via the method arguments.
The Odoo 8 API introduced a [[https://www.odoo.com/documentation/8.0/reference/orm.html#openerp.models.Model.with_context|with_context]] method that accepts keyword arguments for contexts to add:
{{{ lang=python
# Run a model under that context
self.with_context(an_attribute=my_variable).my_method()
# OR
self.env['res.partner'].with_context(an_attribute=my_variable).my_method()
}}}
Or you can update the current context and pass it through instead:
{{{ lang=python
# Copy the context to a dictionary that can be manipulated
context = self._context.copy()
# Add some things
context.update({
'default_user_id': user_id.id,
})
# Run a model under that context
self.with_context(context).my_method()
# OR
self.env['res.partner'].with_context(context).my_method()
}}}
Be careful not to modify the current RecordSet using this functionality:
{{{ lang=python
self = self.env['res.partner'].with_context(context).browse(self.ids)
}}}
The above example will modify the current Records in the RecordSet after a re-browse, which will generate an incoherence between the caches and RecordSet.
= Old Method Signature =
Old API method signatures include a `cr`, `uid`, and `context`. Sometimes they also include `id` or `ids`.
== cr ==
This is the database cursor. For the most part, it is irrelevant while using newer api, but can still be accessed via the environment if needed:
{{{ lang=python
cursor = self._cr
# OR
cursor = self.env.cr
}}}
== uid ==
This is the current user performing the operation. This can also be accessed via the environment if needed:
{{{ lang=python
self.env.user
}}}
=== Changing the User ===
The old API way of changing the user was to pass a different User record to the `uid` param of the model method:
{{{ lang=python
from openerp import SUPERUSER_ID
...
def model_method(self, cr, uid, context=None):
uid = SUPERUSER_ID
self.other_model_method(cr, uid, context)
}}}
The Odoo 8 API cleaned this up quite a bit by the use of a new `sudo` method:
{{{ lang=python
self.sudo(user.id)
self.sudo() # This will use the SUPERUSER_ID by default
# Usage on another model:
self.env['res.partner'].sudo().create(vals)
}}}
== context ==
This is the current environment context, which can now also be accessed via the environment:
{{{ lang=python
self._context
# OR
self.env.context
}}}
See more about the context in [[#environment|Environment]].
Leave a Reply