LasLabs

Odoo 8 API – High Level

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]].


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *