Odoo New API Guide Line
Odoo New API Guide Line
Odoo New API Guide Line
Documentation
Release 0.1
Nicolas Bessi
Contents
.
.
.
.
.
.
3
3
4
4
4
5
5
Environment
2.1 Modifing Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Cleaning Environment Caches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
8
8
Common Actions
3.1 Searching .
3.2 Browsing .
3.3 Writing . .
3.4 Copy . . .
3.5 Create . . .
3.6 Dry run . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
9
10
10
11
11
11
Using Cursor
13
Using Thread
15
New ids
17
Fields
7.1 Field inheritance . .
7.2 Field types . . . . .
7.3 Name Conflicts . . .
7.4 Fields Defaults . . .
7.5 Computed Fields . .
7.6 Inverse . . . . . . .
7.7 Multi Fields . . . .
7.8 Related Field . . . .
7.9 Property Field . . .
7.10 WIP copyable option
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
19
19
20
23
23
23
24
24
24
25
25
27
8.1
8.2
8.3
8.4
8.5
8.6
8.7
8.8
9
@api.returns . .
@api.one . . . .
@api.multi . . .
@api.model . . .
@api.constrains
@api.depends .
@api.onchange .
@api.noguess . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Introspection
10 Conventions
10.1 Snake_casing or CamelCasing . . .
10.2 Imports . . . . . . . . . . . . . . .
10.3 Classes . . . . . . . . . . . . . . .
10.4 New Exceptions classes . . . . . .
10.5 Fields . . . . . . . . . . . . . . . .
10.6 Default or compute . . . . . . . . .
10.7 Modifing self in method . . . . . .
10.8 Doing thing in dry run . . . . . . .
10.9 Using Cursor . . . . . . . . . . . .
10.10 Displayed Name . . . . . . . . . .
10.11 Constraints . . . . . . . . . . . . .
10.12 Qweb view or not Qweb view . . .
10.13 Javascript and Website related code
27
27
28
28
28
28
28
29
31
.
.
.
.
.
.
.
.
.
.
.
.
.
33
33
33
34
34
35
35
35
35
36
36
36
36
36
11 Compatibility
11.1 Access old API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.2 How to be polite with old code base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
37
37
37
12 Unittest
39
13 YAML
41
43
ii
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Overview
Contents
Contents
CHAPTER 1
1.1 Model
A model is a representation of a business Object.
It is basically a class that define various class know-how and fields that are stored in database. All functions defined
in a Model where previously callable directly by the Model.
This paradigm has changed as generally you should not access Model directly but a RecordSet see Recordset
To instantiate a model you must inherit an openerp.model.Model:
from openerp import models, fields, api, _
class MyModel(models.Model):
_name = a.model
firstname = fields.Char(string="Firstname")
1.1.1 Inheritance
The inheritance mechanism has not changed. You can use:
class MyModelExtended(Model):
_inherit = a.model
_inherit = [a.model, a.other.model]
_inherits = {a.model: field_name}
# direct heritage
# direct heritage
# polymorphic heritage
1.2 Recordset
All instances of Model are at the same time instances of a RecordSet. A Recorset represents a sorted set of records of
the same Model as the RecordSet.
You can call function on recordset:
class AModel(Model):
# ...
def a_fun(self):
self.do_something() # here self is a recordset a mix between class and set
record_set = self
record_set.do_something()
def do_something(self):
for record in self:
print record
In this example the functions are defined at model level but when executing the code the self variable is in fact an
instance of RecordSet containing many Records.
So the self passed in the do_something is a RecordSet holding a list of Records.
If you decorate a function with @api.one it will automagically loop on the Records of current RecordSet and self
will this time be the current Record.
As described in Record you have now access to a pseudo Active-Record pattern
Note: If you use it on a RecordSet it will break if recordset does not contains only one item.!!
#
#
#
#
#
#
#
include
not include
extend
union
intersect
difference
to copy recordset (not a deep copy)
1.6 Record
A Record mirrors a populated instance of Model Record fetched from database. It proposes abstraction over database
using caches and query generation:
>>> record = self
>>> record.name
toto
>>> record.partner_id.name
partner name
record = self
record.name = new name
This will update value on the caches and call the write function to trigger a write action on the Database.
On this sample each assignement will trigger a write. As the function is decorated with @api.one for each record in
RecordSet write will be called 3 times. So if you have 10 records in recordset the number of writes will be 10*3 = 30.
This may cause some trouble on an heavy task. In that case you should do:
def better_write(self):
for rec in self:
rec.write({x: 1, y: 2, z: 4})
# or
def better_write2(self):
# same value on all records
self.write({x: 1, y: 2, z: 4})
CHAPTER 2
Environment
In the new API the notion of Environment is introduced. Its main objective is to provide an encapsulation around
cursor, user_id, model, and context, Recordset and caches
With this adjonction you are not anymore forced to pass the infamous function signature:
# before
def afun(self, cr, uid, ids, context=None):
pass
# now
def afun(self):
pass
Environnement sould be immutable and may not be modified in place as it also stores the caches of the RecordSet etc.
It will modifiy the current Records in RecordSet after a rebrowse and will generate an incoherence between caches
and RecordSet.
Chapter 2. Environment
CHAPTER 3
Common Actions
3.1 Searching
Searching has not changed a lot. Sadly the domain changes announced did not meet release 8.0.
You will find main changes below.
3.1.1 search
Now seach function returns directly a RecordSet:
>>> self.search([(is_company, =, True)])
res.partner(7, 6, 18, 12, 14, 17, 19, 8,...)
>>> self.search([(is_company, =, True)])[0].name
Camptocamp
3.1.2 search_read
A search_read function is now available. It will do a search and return a list of dict.
Here we retrieve all partners name:
>>> self.search_read([], [name])
[{id: 3, name: uAdministrator},
{id: 7, name: uAgrolait},
{id: 43, name: uMichel Fletcher},
...]
3.1.3 search_count
The search_count function returns the count of results matching search domain:
>>> self.search_count([(is_company, =, True)])
26L
3.2 Browsing
Browsing is the standard way to obtain Records from the database. Now browsing will return a RecordSet:
>>> self.browse([1, 2, 3])
res.partner(1, 2, 3)
3.3 Writing
3.3.1 Using Active Record pattern
You can now write using Active Record pattern:
@api.one
def any_write(self):
self.x = 1
self.name = a
More info about the subtility of the Active Record write pattern here Record
The classical way of writing is still available.
10
self.line_ids.create({name: Tho})
# this will fail as order is not set
self.line_ids.create({name: Tho, order_id: self.id}) # this will work
self.line_ids.write({name: Tho})
# this will write all related lines
3.4 Copy
Note: Subject to change, still buggy !!!
3.5 Create
Create has not changed, except the fact it now returns a recordset:
self.create({name: New name})
3.4. Copy
11
12
CHAPTER 4
Using Cursor
13
14
CHAPTER 5
Using Thread
When using thread you have to create you own cursor and initiate a new environment for each thread. committing is
done by committing the cursor:
with Environment.manage(): # class function
env = Environment(cr, uid, context)
15
16
CHAPTER 6
New ids
When creating a record a model with computed fields, the records of the recordset will be in memory only. At that
time the id of the record will be a dummy ids of type openerp.models.NewId
So if you need to use the record id in your code (e.g. for a sql query) you should check if it is available:
if isinstance(current_record.id, models.NewId):
# do your stuff
17
18
CHAPTER 7
Fields
class AModel(models.Model):
_name = a_name
name = fields.Char(
string="Name",
compute="_compute_name_custom",
store=True,
select=True,
readonly=True,
inverse="_write_name"
required=True,
translate=True,
help=blabla,
company_dependent=True,
search=_search_function
)
#
#
#
#
#
#
#
#
#
#
#
Valid definition
19
7.2.2 Char
Store string with variable len.:
achar = fields.Char()
Specific options:
size: data will be trimmed to specified size
translate: field can be translated
7.2.3 Text
Used to store long text.:
atext = fields.Text()
Specific options:
translate: field can be translated
7.2.4 HTML
Used to store HTML, provides an HTML widget.:
anhtml = fields.HTML()
Specific options:
translate: field can be translated
7.2.5 Integer
Store integer value. No NULL value support. If value is not set it returns 0:
anint = fields.Integer()
20
Chapter 7. Fields
7.2.6 Float
Store float value. No NULL value support. If value is not set it returns 0.0 If digits option is set it will use numeric
type:
afloat = fields.Float()
afloat = fields.Float(digits=(32, 32))
afloat = fields.Float(digits=lambda cr: (32, 32))
Specific options:
digits: force use of numeric type on database. Parameter can be a tuple (int len, float len) or a callable that return
a tuple and take a cursor as parameter
7.2.7 Date
Store date. The field provides some helpers:
context_today returns current day date string based on tz
today returns current system date string
from_string returns datetime.date() from string
to_string returns date string from datetime.date
:
>>> from openerp import fields
>>> adate = fields.Date()
>>> fields.Date.today()
2014-06-15
>>> fields.Date.context_today(self)
2014-06-15
>>> fields.Date.context_today(self, timestamp=datetime.datetime.now())
2014-06-15
>>> fields.Date.from_string(fields.Date.today())
datetime.datetime(2014, 6, 15, 19, 32, 17)
>>> fields.Datetime.to_string(datetime.datetime.today())
2014-06-15
7.2.8 DateTime
Store datetime. The field provide some helper:
context_timestamp returns current day date string based on tz
now returns current system date string
from_string returns datetime.date() from string
to_string returns date string from datetime.date
:
21
>>> fields.Datetime.from_string(fields.Datetime.now())
datetime.datetime(2014, 6, 15, 19, 32, 17)
>>> fields.Datetime.to_string(datetime.datetime.now())
2014-06-15 19:26:13
7.2.9 Binary
Store file encoded in base64 in bytea column:
abin = fields.Binary()
7.2.10 Selection
Store text in database but propose a selection widget. It induces no selection constraint in database. Selection must be
set as a list of tuples or a callable that returns a list of tuples:
aselection = fields.Selection([(a, A)])
aselection = fields.Selection(selection=[(a, A)])
aselection = fields.Selection(selection=a_function_name)
Specific options:
selection: a list of tuple or a callable name that take recordset as input
7.2.11 Reference
Store an arbitrary reference to a model and a row:
aref = fields.Reference([(model_name, String)])
aref = fields.Reference(selection=[(model_name, String)])
aref = fields.Reference(selection=a_function_name)
Specific options:
selection: a list of tuple or a callable name that take recordset as input
7.2.12 Many2one
Store a relation against a co-model:
arel_id = fields.Many2one(res.users)
arel_id = fields.Many2one(comodel_name=res.users)
Specific options:
comodel_name: name of the opposite model
7.2.13 One2many
Store a relation against many rows of co-model:
arel_ids = fields.One2many(res.users, rel_id)
arel_ids = fields.One2many(comodel_name=res.users, inverse_name=rel_id)
22
Chapter 7. Fields
Specific options:
comodel_name: name of the opposite model
inverse_name: relational column of the opposite model
7.2.14 Many2many
Store a relation against many2many rows of co-model:
arel_ids = fields.Many2many(res.users)
arel_ids = fields.Many2many(comodel_name=res.users,
relation=table_name,
column1=col_name,
column2=other_col_name)
Specific options:
comodel_name: name of the opposite model
relation: relational table name
columns1: relational table left column name
columns2: relational table right column name
Using a fun will force you to define function before fields definition.
23
class AModel(models.Model):
_name = a_name
computed_total = fields.Float(compute=compute_total)
def compute_total(self):
...
self.computed_total = x
The function can be void. It should modify record property in order to be written to the cache:
self.name = new_value
Be aware that this assignation will trigger a write into the database. If you need to do bulk change or must be careful
about performance, you should do classic call to write
To provide a search function on a non stored computed field you have to add a search kwarg on the field. The value
is the name of the function as a string or a reference to a previously defined method. The function takes the second
and third member of a domain tuple and returns a domain itself
def search_total(self, operator, operand):
...
return domain # e.g. [(id, in, ids)]
7.6 Inverse
The inverse key allows to trigger call of the decorated function when the field is written/created
24
Chapter 7. Fields
Note: When updating any related field not all translations of related field are translated if field is stored!!
Chained related fields modification will trigger invalidation of the cache for all elements of the chain.
25
26
Chapter 7. Fields
CHAPTER 8
New decorators are just mapper around the new API. The decorator are mandatory as webclient and HTTP controller
are not compliant with new API.
api namespace decorators will detect signature using variable name and decide to match old signature or not.
Recognized variable names are:
cr, cursor, uid, user, user_id, id, ids, context
8.1 @api.returns
This decorator guaranties unity of returned value. It will return a RecordSet of specified model based on original
returned value:
@api.returns(res.partner)
def afun(self):
...
return x # a RecordSet
And if an old API function calls a new API function it will automatically convert it into a list of ids
All decorators inherits from this decorator to upgrade or downgrade the returned value.
8.2 @api.one
This decorator loops automatically on Records of RecordSet for you. Self is redefined as current record:
@api.one
def afun(self):
self.name = toto
Note: Caution: the returned value is put in a list. This is not always supported by the web client, e.g. on button action
methods. In that case, you should use @api.multi to decorate your method, and probably call self.ensure_one() in
the method definition.
27
8.3 @api.multi
Self will be the current RecordSet without iteration. It is the default behavior:
@api.multi
def afun(self):
len(self)
8.4 @api.model
This decorator will convert old API calls to decorated function to new API signature. It allows to be polite when
migrating code.
@api.model
def afun(self):
pass
8.5 @api.constrains
This decorator will ensure that decorated function will be called on create, write, unlink operation. If a constraint is
met the function should raise a openerp.exceptions.Warning with appropriate message.
8.6 @api.depends
This decorator will trigger the call to the decorated function if any of the fields specified in the decorator is altered by
ORM or changed in the form:
@api.depends(name, an_other_field)
def afun(self):
pass
Note: when you redefine depends you have to redefine all @api.depends, so it loses some of his interest.
8.7 @api.onchange
This decorator will trigger the call to the decorated function if any of the fields specified in the decorator is changed in
the form:
@api.onchange(fieldx):
def do_stuff(self):
if self.fieldx == x:
self.fieldy = toto
28
In previous sample self corresponds to the record currently edited on the form. When in on_change context all work
is done in the cache. So you can alter RecordSet inside your function without being worried about altering database.
Thats the main difference with @api.depends
At function return, differences between the cache and the RecordSet will be returned to the form.
8.8 @api.noguess
This decorator prevent new API decorators to alter the output of a method
8.8. @api.noguess
29
30
CHAPTER 9
Introspection
A common pattern in OpenERP was to do Model fields introspection using _columns property. From 8.0
_columns is deprecated by _fields that contains list of consolidated fields instantiated using old or new API.
Conventions and code update
31
32
Chapter 9. Introspection
CHAPTER 10
Conventions
10.2 Imports
As discussed with Raphal Collet. This convention should be the one to use after RC1.
10.2.1 Model
from openerp import models
10.2.2 Fields
from openerp import fields
10.2.3 Translation
from openerp import _
10.2.4 API
from openerp import api
10.2.5 Exceptions
from openerp import exceptions
33
10.3 Classes
Class should be initialized like this:
class Toto(models.Model):
pass
class Titi(models.TransientModel):
pass
10.4.1 RedirectWarning
Warning with a possibility to redirect the user instead of simply diplaying the warning message.
Should receive as parameters:
param string button_text text to put on the button that will trigger the redirection.
10.4.2 AccessDenied
Login/password error. No message, no traceback.
10.4.3 AccessError
Access rights error.
34
10.4.5 DeferredException:
Exception object holding a traceback for asynchronous reporting.
Some RPC calls (database creation and report generation) happen with an initial request followed by multiple, polling
requests. This class is used to store the possible exception occurring in the thread serving the first request, and is then
sent to a polling request.
Note: Traceback is misleading, this is really a sys.exc_info() triplet.
10.4.6 Compatibility
When catching orm exception we should catch both types of exceptions:
try:
pass
except (Warning, except_orm) as exc:
pass
10.5 Fields
Fields should be declared using new fields API. Putting string key is better than using a long property name:
class AClass(models.Model):
name = fields.Char(string="This is a really long long name")
really_long_long_long_name = fields.Char()
# ok
That said the property name must be meaningful. Avoid name like nb etc.
10.5. Fields
35
10.11 Constraints
Should be done using @api.constrains decorator in conjunction with the @api.one if performance allows it.
36
CHAPTER 11
Compatibility
There is some pattern to know during the transition period to keep code base compatible with both old and new API.
37
38
CHAPTER 12
Unittest
To get access to the new API in unittest inside common.TransactionCase and others:
class test_partner_firstname(common.TransactionCase):
def setUp(self):
super(test_partner_firstname, self).setUp()
self.user_model = self.env["res.users"]
self.partner_model = self.env["res.partner"]
39
40
CHAPTER 13
YAML
41
42
CHAPTER 14
genindex
modindex
search
43