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.



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:

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:

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.

Check out this article for more information on inheritance mechanisms.

Model Inheritance Types:

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

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:

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:

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:

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

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

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:

Odoo Environment High Level

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:

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 with_context method that accepts keyword arguments for contexts to add:

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

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

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:

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:

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:

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:

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:

self._context
# OR
self.env.context

See more about the context in Environment.

0