from openerp import models, fields, api, _
class MyModel(models.Model):
_name = 'mymodule.mymodel'
# Fields are declared as class attributes:
char = fields.Char('Char', 64) # name, size
text = fields.Text('Text')
intg = fields.Integer('Integer')
flot = fields.Float('Float', (16,4)) # name, digits
flag = fields.Boolean('Boolean')
date = fields.Date('Date')
time = fields.Datetime('Date and Time')
html = fields.HTML('Hypertext')
biny = fields.Binary('Binary')
A field is defined as class attribute on a model class. If the model is extended, it's fields can also be extended, the field attributes are taken from the parent class and can then be overridden in the subclass.
Common parameters:
string
: field label on views. Can be passed as first parameter without using a keyword (string).size
on Char: the maximum size of values stored for that field (integer, optional).translate
on Char,Text: values are translatable (boolean).digits
on Float : a pair (total, decimal), or a function taking a database cursor and returning a pair (total, decimal).help
: tooltip seen by users (string).readonly
: set field as readonly (boolean).required
: set field as mandatory (boolean).index
: make the field indexed in the database (boolean).default
: default value; either a static value or a function taking a recordset and returning a value.states
: dictionary mapping state values to lists of attribute-value pairs ('readonly', 'required' or 'invisible').groups
: comma-separated list of group xml ids (string); restricts field access to those user groups.company_dependent
: value depends on the company (boolean). Maps to v7 property field.
selc = fields.Selection([('code','Desc'),...], 'Label')
refr = fields.Reference([('a.model', 'A Model'),...], 'Label')
m2o = fields.Many2one('amodule.amodel', 'Related Value') # comodel, string
o2m = fields.One2many('amodule.amodel', 'inverse_id', 'Related List')
comodel_name
: identifier name of the target model (string).inverse_name
on One2many: the inverseMany2one
field incomodel_name
(string).limit
on One2many: optional limit to use upon read (integer).relation
on Many2many:optional name of the table that stores the relation in the database (string).column1
on Many2many: optional name of the column referring to "these" records in the relation table (string).column2
on Many2many: optional name of the column referring to "those" records in the relationtable (string).domain
: optional domain to filter candidate values on client side (domain list or string).context
: optional context to use on the client side (dictionary).ondelete
: what to do when referred record is deleted:set null
,restrict
,cascade
auto_join
: use JOINs when following relation, but skips security on related model (boolean).delegate
: make fields of the target model accessible; same as v7_inherits
(boolean).
Are defined providing a value for the attribute compute
.
compute
: name of a model method that computes the fieldinverse
: name of a model method that inverses the field (optional)search
: name of a model method that implement search on the field (optional)store
: whether the field is stored in database (boolean, by defaultFalse
)
compute
, inverse
and search
are model methods with signatures:
upper = fields.Char(compute='_compute_upper',
inverse='_inverse_upper',
search='_search_upper')
@api.depends('name')
def _compute_upper(self):
for rec in self:
self.upper = self.name.upper() if self.name else False
def _inverse_upper(self):
for rec in self:
self.name = self.upper.lower() if self.upper else False
def _search_upper(self, operator, value):
if operator == 'like':
operator = 'ilike'
return [('name', operator, value)]
The compute method has to assign the field on all records of the invoked
recordset. The decorator :meth:openerp.api.depends
must be applied on
the compute method to specify the field dependencies; those dependencies
are used to determine when to recompute the field; recomputation is
automatic and guarantees cache/database consistency. Note that the same
method can be used for several fields, you simply have to assign all the
given fields in the method; the method will be invoked once for all
those fields.
By default, a computed field is not stored to the database, and is
computed on-the-fly. Adding the attribute store=True
will store the
field's values in the database. The advantage of a stored field is that
searching on that field is done by the database itself. The disadvantage
is that it requires database updates when the field must be recomputed.
The inverse method, as its name says, does the inverse of the compute method: the invoked records have a value for the field, and you must apply the necessary changes on the field dependencies such that the computation gives the expected value. Note that a computed field without an inverse method is readonly by default.
The search method is invoked when processing domains before doing an
actual search on the model. It must return a domain equivalent to the
condition: field operator value
.
from openerp import models, fields, api, _
class MyModel(models.Model):
_name = 'module.mymodel'
name = fields.Char(required=True)
parent = fields.Many2one('test_new_api.category')
display_name = fields.Char(compute='_compute_display_name', inverse='_inverse_display_name')
@api.one
@api.depends('name', 'parent.display_name') # this definition is recursive
def _compute_display_name(self):
if self.parent:
self.display_name = self.parent.display_name + ' / ' + self.name
else:
self.display_name = self.name
@api.one
def _inverse_display_name(self):
names = self.display_name.split('/')
# determine sequence of categories
categories = []
for name in names[:-1]:
category = self.search([('name', 'ilike', name.strip())])
categories.append(category[0])
categories.append(self)
# assign parents following sequence
for parent, child in zip(categories, categories[1:]):
if parent and child:
child.parent = parent
# assign name of last category, and reassign display_name (to normalize it)
self.name = names[-1].strip()
```