Fields¶
Based on: http://odoo-new-api-guide-line.readthedocs.io/en/latest/fields.html
Now fields are class property:
from openerp import models, fields
class AModel(models.Model):
_name = 'a_name'
name = fields.Char(
string="Name", # Optional label of the field
compute="_compute_name_custom", # Transform the fields in computed fields
store=True, # If computed it will store the result
select=True, # Force index on field
readonly=True, # Field will be readonly in views
inverse="_write_name" # On update trigger
required=True, # Mandatory field
translate=True, # Translation enable
help='blabla', # Help tooltip text
company_dependent=True, # Transform columns to ir.property
search='_search_function' # Custom search function mainly used with compute
)
# The string key is not mandatory
# by default it wil use the property name Capitalized
name = fields.Char() # Valid definition
Field inheritance¶
One of the new features of the API is to be able to change only one attribute of the field:
name = fields.Char(string='New Value')
Field types¶
Char¶
Store string with variable len.:
achar = fields.Char()
Specific options:
- size: data will be trimmed to specified size
- translate: field can be translated
Text¶
Used to store long text.:
atext = fields.Text()
Specific options:
- translate: field can be translated
HTML¶
Used to store HTML, provides an HTML widget.:
anhtml = fields.Html()
Specific options:
- translate: field can be translated
Integer¶
Store integer value. No NULL value support. If value is not set it returns 0:
anint = fields.Integer()
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
Date¶
Store date. The field provides some helpers:
context_today
returns current day date string based on tztoday
returns current system date stringfrom_string
returns datetime.date() from stringto_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.Date.to_string(datetime.datetime.today())
'2014-06-15'
DateTime¶
Store datetime. The field provide some helper:
context_timestamp
returns current day date string based on tznow
returns current system date stringfrom_string
returns datetime.date() from stringto_string
returns date string from datetime.date
:
>>> fields.Datetime.context_timestamp(self, timestamp=datetime.datetime.now())
datetime.datetime(2014, 6, 15, 21, 26, 1, 248354, tzinfo=<DstTzInfo 'Europe/Brussels' CEST+2:00:00 DST>)
>>> fields.Datetime.now()
'2014-06-15 19:26:13'
>>> 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'
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
- size: the option size=1 is mandatory when using indexes that are integers, not strings
When extending a model, if you want to add possible values to a selection field, you may use the selection_add keyword argument:
class SomeModel(models.Model):
_inherits = 'some.model'
type = fields.Selection(selection_add=[('b', 'B'), ('c', 'C')])
Since Odoo 14.0 you have to specify ondelete attribute.
ondelete
provides a fallback mechanism for any overridden- field with a selection_add. It is a dict that maps every option from the selection_add to a fallback action. This fallback action will be applied to all records whose selection_add option maps to it. The actions can be any of the following:
'set null'
– the default, all records with this option will have their selection value set to False.'cascade'
– all records with this option will be deleted along with the option itself.'set default'
– all records with this option will be set to the default of the field definition<callable>
– a callable whose first and only argument will be the set of records containing the specified Selection option, for custom processing. e.g.:my_selection = fields.Selection(selection_add=[ ('bacon', "Bacon"), ], ondelete={'bacon': lambda records: record.write({'my_selection': 'bar'})})
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
Many2one¶
Store a relation against a co-model:
arel_id = fields.Many2one('res.users')
arel_id = fields.Many2one(comodel_name='res.users')
an_other_rel_id = fields.Many2one(comodel_name='res.partner', delegate=True)
Specific options:
- comodel_name: name of the opposite model
- delegate: set it to
True
to make fields of the target model accessible from the current model (corresponds to_inherits
)
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')
Specific options:
- comodel_name: name of the opposite model
- inverse_name: relational column of the opposite model
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 (reference to record in current table)
- columns2: relational table right column name (reference to record in comodel_name table)
In order to make two mutual many2many fields in different models use in them the same relation table and inverse columns:
_name = 'model1'
model2_ids = fields.Many2many(
'model2', 'model2_ids_model1_ids_rel', 'model2_id', 'model1_id',
_name = 'model2'
model1_ids = fields.Many2many(
'model1', 'model2_ids_model1_ids_rel', 'model1_id', 'model2_id',
Name Conflicts¶
Note
fields and method name can conflict.
When you call a record as a dict it will force to look on the columns.
Fields Defaults¶
Default is now a keyword of a field:
You can attribute it a value or a function
name = fields.Char(default='A name')
# or
name = fields.Char(default=a_fun)
#...
def a_fun(self):
return self.do_something()
Using a fun will force you to define function before fields definition.
Note. Default value cannot depend on values of other fields of a record, i.e. you cannot read other fields via self
in the function.
Computed Fields¶
There is no more direct creation of fields.function.
Instead you add a compute
kwarg. The value is the name of the function as a string or a function.
This allows to have fields definition atop of class:
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)]
Inverse¶
The inverse key allows to trigger call of the decorated function when the field is written/”created”
Multi Fields¶
To have one function that compute multiple values:
@api.multi
@api.depends('field.relation', 'an_otherfield.relation')
def _amount(self):
for x in self:
x.total = an_algo
x.untaxed = an_algo
Property Field¶
There is some use cases where value of the field must change depending of the current company.
To activate such behavior you can now use the company_dependent option.
A notable evolution in new API is that “property fields” are now searchable.
WIP copyable option¶
There is a dev running that will prevent to redefine copy by simply setting a copy option on fields:
copy=False # !! WIP to prevent redefine copy