139 lines
3.7 KiB
ReStructuredText
139 lines
3.7 KiB
ReStructuredText
==================
|
|
High-level ORM API
|
|
==================
|
|
|
|
.. _compute:
|
|
|
|
Computed fields: defaults and function fields
|
|
=============================================
|
|
|
|
The high-level API attempts to unify concepts of programmatic value generation
|
|
for function fields (stored or not) and default values through the use of
|
|
computed fields.
|
|
|
|
Fields are marked as computed by setting their ``compute`` attribute to the
|
|
name of the method used to compute then::
|
|
|
|
has_sibling = fields.Integer(compute='compute_has_sibling')
|
|
|
|
by default computation methods behave as simple defaults in case no
|
|
corresponding value is found in the database::
|
|
|
|
def default_number_of_employees(self):
|
|
self.number_of_employees = 1
|
|
|
|
.. todo::
|
|
|
|
literal defaults::
|
|
|
|
has_sibling = fields.Integer(compute=fields.default(1))
|
|
|
|
but they can also be used for computed fields by specifying fields used for
|
|
the computation. The dependencies can be dotted for "cascading" through
|
|
related models::
|
|
|
|
@api.depends('parent_id.children_count')
|
|
def compute_has_sibling(self):
|
|
self.has_sibling = self.parent_id.children_count >= 2
|
|
|
|
.. todo::
|
|
|
|
function-based::
|
|
|
|
has_sibling = fields.Integer()
|
|
@has_sibling.computer
|
|
@api.depends('parent_id.children_count')
|
|
def compute_has_sibling(self):
|
|
self.has_sibling = self.parent_id.children_count >= 2
|
|
|
|
note that computation methods (defaults or others) do not *return* a value,
|
|
they *set* values the current object. This means the high-level API does not
|
|
need :ref:`an explicit multi <fields-functional>`: a ``multi`` method is
|
|
simply one which computes several values at once::
|
|
|
|
@api.depends('company_id')
|
|
def compute_relations(self):
|
|
self.computed_company = self.company_id
|
|
self.computed_companies = self.company_id.to_recordset()
|
|
|
|
Automatic onchange
|
|
==================
|
|
|
|
Using to the improved and expanded :ref:`computed fields <compute>`, the
|
|
high-level ORM API is able to infer the effect of fields on
|
|
one another, and thus automatically provide a basic form of onchange without
|
|
having to implement it by hand, or implement dozens of onchange functions to
|
|
get everything right.
|
|
|
|
|
|
|
|
|
|
.. todo::
|
|
|
|
deferred records::
|
|
|
|
partner = Partner.record(42, defer=True)
|
|
partner.name = "foo"
|
|
partner.user_id = juan
|
|
partner.save() # only saved to db here
|
|
|
|
with scope.defer():
|
|
# all records in this scope or children scopes are deferred
|
|
# until corresponding scope poped or until *this* scope poped?
|
|
partner = Partner.record(42)
|
|
partner.name = "foo"
|
|
partner.user_id = juan
|
|
# saved here, also for recordset &al, ~transaction
|
|
|
|
# temp deferment, maybe simpler? Or for bulk operations?:
|
|
with Partner.record(42) as partner:
|
|
partner.name = "foo"
|
|
partner.user_id = juan
|
|
|
|
``id = False`` => always defered? null v draft?
|
|
|
|
.. todo:: keyword arguments passed positionally (common for context, completely breaks everything)
|
|
|
|
.. todo:: optional arguments (report_aged_receivable)
|
|
|
|
.. todo:: non-id ids? (mail thread_id)
|
|
|
|
.. todo:: partial signatures on overrides (e.g. message_post)
|
|
|
|
.. todo::
|
|
|
|
::
|
|
|
|
field = fields.Char()
|
|
|
|
@field.computer
|
|
def foo(self):
|
|
"compute foo here"
|
|
|
|
~
|
|
|
|
::
|
|
|
|
field = fields.Char(compute='foo')
|
|
|
|
def foo(self):
|
|
"compute foo here"
|
|
|
|
.. todo:: doc
|
|
|
|
.. todo:: incorrect dependency spec?
|
|
|
|
.. todo:: dynamic dependencies?
|
|
|
|
::
|
|
|
|
@api.depends(???)
|
|
def foo(self)
|
|
self.a = self[self.b]
|
|
|
|
.. todo:: recursive onchange
|
|
|
|
Country & state. Change country -> remove state; set state -> set country
|
|
|
|
.. todo:: onchange list affected?
|