Files
tryton/doc/models.rst
T

826 lines
25 KiB
ReStructuredText
Raw Normal View History

2008-12-02 11:21:37 +01:00
:tocdepth: 2
Models
2008-09-24 19:03:48 +02:00
######
2008-09-25 00:53:40 +02:00
Models are python classes that usually represent business object or
2008-09-24 19:03:48 +02:00
concepts. Such classes consist essentially of keywords, fields,
constraints and helper functions. They inherit from the OSV class
which provide the framework integration like the database abstraction,
2008-09-29 15:29:45 +02:00
workflows, translations, etc.
2008-09-24 19:03:48 +02:00
2008-09-26 19:18:49 +02:00
The following snippet gives a first idea of what can be done:
2008-09-24 19:03:48 +02:00
.. highlight:: python
::
2008-09-26 00:32:22 +02:00
class Category(OSV):
"Party category"
_name = 'relationship.category'
_description = __doc__
name = fields.Char('Name')
Category()
class Country(OSV):
"Country"
_name = 'relationship.country'
_description = __doc__
name = fields.Char('Name')
Category()
class Address(OSV):
"Address"
_name = 'relationship.address'
_description = __doc__
name = fields.Char('Contact Name')
party = fields.Many2One('relationship.party', 'Party')
country = fields.Many2One('relationship.country', 'Country')
city = fields.Char('City')
Address()
2008-09-24 19:03:48 +02:00
class Party(OSV):
"Party"
_description = __doc__
_name = "relationship.party"
name = fields.Char('Name')
2008-09-26 00:32:22 +02:00
addresses = fields.One2Many('relationship.address', 'party', 'Addresses')
categories = fields.Many2Many('relationship.category', 'relationship_category_rel',
'party', 'category', 'Categories')
2008-09-24 19:03:48 +02:00
Party()
2008-12-23 22:41:17 +01:00
Instantiating the class makes it alive for the framework. Actually
2008-09-25 00:53:40 +02:00
there will be only one instance per class and per database. So model
2008-09-26 00:32:22 +02:00
instances are essentially accessors to the underlying table. Columns of
the table correspond to the fields of the class. Rows of the table
corresponds to records.
2008-09-24 19:03:48 +02:00
2008-09-25 00:53:40 +02:00
Model Properties
****************
Model properties defines meta-informations about the model, they are
class attributes starting with and underscore.
2008-09-24 19:03:48 +02:00
2008-09-26 00:32:22 +02:00
* ``_description``: A non technical description of the model.
2008-09-24 19:03:48 +02:00
* ``_name``: The unique identifier that can be use to reference the
2008-09-29 15:29:45 +02:00
model across all the framework.
2008-09-24 19:03:48 +02:00
* ``_table``: The name of the database table which is mapped to
the current class. If not set the value of ``_name`` is used with
dots converted to underscores.
2008-09-26 00:32:22 +02:00
* ``_rec_name``: The name of the main field of the model. If not
2008-09-24 19:03:48 +02:00
set the field ``name`` is used (like in the above example).
2008-09-25 00:53:40 +02:00
* ``_sequence``: The name of the postgresql sequence that
increment the ``id`` column.
2008-09-24 19:03:48 +02:00
2008-09-26 00:32:22 +02:00
* ``_order_name``: The name of the field (or an sql expression) on
which the row must be sorted when the underlying table is read.
2008-09-24 19:03:48 +02:00
* ``_auto``: When set to ``False`` will prevent the framework from
creating a table for this class.
* ``_sql``: The sql code that must be used to fetch data from the
2008-09-29 15:29:45 +02:00
database. The columns outputted by the given query must reflect
2008-09-24 19:03:48 +02:00
the class fields.
2008-09-25 00:53:40 +02:00
Some Model Properties are instance attributes which allow to update
2008-09-26 19:18:49 +02:00
them at other places in the framework
2008-09-25 00:53:40 +02:00
* ``_order``: A tuple defining how the row are sorted when the
underlying table is read. E.g.: ``[('name', 'ASC'), 'age',
'DESC']``
* ``_error_messages``: A dictionary mapping keywords to a user
message. E.g.: ``{'recursive_categories': 'You can not create
recursive categories!'}``
* ``_constraints``: A list of constraints that each record must
respect. Each item of this list is a couple ``('function_name',
'error_keyword')``, where ``'function_name'`` is the name of a
method of the same class, which should return a boolean value
(``False`` when the constraint is violated). ``error_keyword``
must be one of the key of ``_sql_error_messages``.
2008-09-26 00:32:22 +02:00
* ``_sql_constraints``: A list of constraints that are added on
the underlying table. E.g.: ``('constrain_name, sql_constraint,
'error_msg')`` where ``'constrain_name'`` is the name of the
sql constraint for the database, ``sql_constraint`` is the actual
sql constraint (E.g.: ``'UNIQUE(name)'``) and ``'error_msg'`` is
one of the key of ``_error_messages``.
2008-09-25 00:53:40 +02:00
2008-09-26 00:32:22 +02:00
* ``_sql_error_messages``: Like ``_error_messages`` for
``_sql_constraints``.
* ``_rpc_allowed``: The list of the names of the method that are
allowed to be called remotely. By default it contains: ``[
'read', 'write', 'create', 'default_get', 'delete', 'fields_get',
'fields_view_get', 'search', 'name_get', 'name_search', 'copy',
'import_data', 'export_data', 'search_count', 'search_read']``
2008-09-26 12:04:57 +02:00
and all the ``default_*`` and ``on_change_*`` methods.
2008-09-25 00:53:40 +02:00
2008-09-24 19:03:48 +02:00
Fields
******
2008-09-25 00:53:40 +02:00
Fields are class attributes that do not start with an underscore. Some
2008-09-26 19:18:49 +02:00
fields are automatically added on each tables:
2008-09-26 00:32:22 +02:00
* ``id``: An integer providing a identifier for each row.
2008-09-26 12:04:57 +02:00
* ``create_date``: The date at which the row was created.
2008-09-26 00:32:22 +02:00
* ``create_uid``: The identifier of the user (I.e. the ``id`` of
one row of the table defining the users) who created the row.
* ``write_date``: The date of the last modification.
* ``write_uid``: The user who made the last modification.
2008-09-25 00:53:40 +02:00
2008-09-24 19:03:48 +02:00
Fields types
^^^^^^^^^^^^
A field can be one of the following basic types:
* ``Char``: A string of character.
2008-09-25 00:53:40 +02:00
* ``Text``: A multi-line text.
2008-09-24 19:03:48 +02:00
* ``Boolean``: True or False.
* ``Integer``: An integer number.
2008-10-24 00:52:56 +02:00
* ``BigInteger``: An integer number with a larger range.
2008-09-24 19:03:48 +02:00
* ``Float``: A floating point number.
* ``Numeric``: Like Float, but provide an arbitrary precision on
all operations.
* ``Date``: A day. E.g.: 2008-12-31.
* ``DateTime``: A time in a day. E.g.: 2008-12-31 11:30:59.
* ``Selection``: A value from a list. See :ref:`define_selection`
for details.
2008-09-24 19:03:48 +02:00
* ``Binary``: A blob. E.g.: a picture.
* ``Sha``: Like a char but his content is never shown to the
user. The typical usage is for password fields.
Or one of these relation types:
2008-09-26 00:32:22 +02:00
* ``Many2One``: A relation from the current model to another one
where several record of the current model can be linked to the
same record of the other. E.g.: ``party =
fields.Many2One('relationship.party', 'Party')`` where
``'relationship.party'`` is the identifier of the other
model. This correspond in the database to a foreign key from the
2008-09-26 19:18:49 +02:00
table of the current model to the ``relationship_party``
table. See :ref:`define_tree` for advanced usage.
2008-09-24 19:03:48 +02:00
2008-09-26 00:32:22 +02:00
* ``One2Many``: A relation from the current model to another one
where one record of the current model can be linked to many
records of the other. E.g.: ``addresses =
fields.One2Many('relationship.party', 'party',
'Addresses')``. This correspond in the database to a foreign key
(who's name is ``party``) from the ``relationship_address`` table
2008-09-26 12:04:57 +02:00
to the table of the current model. A ``One2Many`` alone will not
2008-12-24 01:05:31 +01:00
work, it relies on the ``Many2One`` to create the foreign key.
2008-09-24 19:03:48 +02:00
2008-09-26 00:32:22 +02:00
* ``Many2Many``: A relation from the current model to another one
where many record of the current model can be linked to many
records of the other. E.g.: ``categories =
fields.Many2Many('relationship.category',
'relationship_category_rel', 'party', 'category',
'Categories')``. This correspond in the database to a new table
2008-09-29 15:29:45 +02:00
``relationship_category_rel`` with two foreign key ``party`` and
2008-09-26 00:32:22 +02:00
``category`` pointing to ``relationship_party`` and
``relationship_category``.
2008-09-24 19:03:48 +02:00
2008-09-29 15:29:45 +02:00
Or one of these composed types:
2008-12-24 11:27:23 -08:00
* ``Property``: Like a ``Many2One`` but allows complex usage of
2008-09-29 15:29:45 +02:00
default value: when the default value is updated, all the fields
with this default value are updated. When the company module is
2008-12-24 11:27:23 -08:00
installed it also allows the definition of different values for
the same field depending on the company of the user.
2008-09-29 15:29:45 +02:00
2008-12-24 11:27:23 -08:00
* ``Reference``: Also like a ``Many2One``. It allows the creation of
a link to an arbitrary selection of other models. This selection is
2008-10-01 21:13:33 +02:00
defined by the records of ``res.request.link``.
2008-09-29 15:29:45 +02:00
2008-09-24 19:03:48 +02:00
Function field can be used to mimic any other type:
2008-09-26 00:32:22 +02:00
* ``Function``: A computed field. E.g. ``total =
fields.Function('get_total', type='float',
string='Total')``. Where ``'get_total'`` is the name if a method
2008-09-26 19:18:49 +02:00
of the current class. See :ref:`use_function` for more details.
.. _define_selection:
How to define selections
++++++++++++++++++++++++
A selection field allow the user to choose one value across a limited
2008-10-06 14:06:10 +02:00
number of values. There are two ways to define a field that will be
shown as a selection to the user:
1. Using the fields.Selection()
2008-10-06 14:06:10 +02:00
2. Using a fields.Many2One() (with a static domain) and define it in
the xml view of the module as ``widget="Selection"``, see
:ref:`example_form_view`.
Let's take the following model as an example of the first option:
.. highlight:: python
::
class Truck(OSV):
_name = "truck"
_rec_name = "registration"
registration = fields.Char('Registration number', required=True)
color = fields.Selection(
[("white", "White"),
("black", "Black"),
], "Color")
Truck ()
2008-12-24 11:27:23 -08:00
The selection is defined as list of tuples. The first member of each tuple
will be the value to store in the database when the second value is selected
through the user interface. The second member is the string (or one of it's
translations) that will be displayed in the UI.
By default the selection is sorted when presented to the user. In some
2008-12-24 11:27:23 -08:00
cases it could be useful to avoid this behaviour. In such a case one can
use the ``sort`` argument:
.. highlight:: python
::
size = fields.Selection([("small","Small"),
("average", "Average"),
("big", "Big")], "Size", sort=False)
If we want overwrite the list of available colors in another module, the
following will work as expected (See :ref:`define_inheritance`):
.. highlight:: python
::
class Truck(OSV):
_name = "truck"
color = fields.Selection(
[("white", "White"),
("black", "Black"),
("red", "Red"),
], "Color")
Truck ()
But this can create problems if a third module wants to add other
2008-12-24 11:27:23 -08:00
colors or if the base module evolves.
2008-12-24 11:27:23 -08:00
A better solution for this case would be to extend the list:
.. highlight:: python
::
class Truck(OSV):
_name = "truck"
def __init__(self):
super(Truck, self).__init__()
self.color.selection += [('red', 'Red')]
Truck ()
2008-09-26 19:18:49 +02:00
.. _define_tree:
How to define tree structures
+++++++++++++++++++++++++++++
Todo: letf, right and child_of.
.. _use_function:
How to use function fields
++++++++++++++++++++++++++
Let's say that the following field is defined on the invoice model:
.. highlight:: python
::
total = fields.Function('get_total', type='float', string='Total')
The ``get_total`` method should look like this:
.. highlight:: python
::
def get_total(self, cursor, user, ids, name, arg, context=None):
res = {}.fromkeys(ids, 0.0)
for invoice in self.browse(cursor, user, ids, context=context):
for line in invoice:
if invoice.id in res:
res[invoice.id] += line.amount
else:
res[invoice.id] = line.amount
return res
2008-09-29 15:29:45 +02:00
One should note that the dictionary ``res`` should map a value for
2008-09-26 19:18:49 +02:00
each id in ``ids``.
One method to rule them all
````````````````````````````
2008-12-24 11:27:23 -08:00
The first variant of this we'll look at is defining a unique function for
several fields. Let's consider this new field which returns the total for
the invoice lines of kind *service*:
2008-09-26 19:18:49 +02:00
.. highlight:: python
::
total_service = fields.Function('get_total', type='float', string='Total Service')
2008-12-24 11:27:23 -08:00
For this field the method ``get_total`` can be defined this way:
2008-09-26 19:18:49 +02:00
.. highlight:: python
::
def get_total(self, cursor, user, ids, name, arg, context=None):
res = {}.fromkeys(ids, 0.0)
for invoice in self.browse(cursor, user, ids, context=context):
for line in invoice:
if name == 'total_service' and line.kind != "service":
continue
if invoice.id in res:
res[invoice.id] += line.amount
else:
res[invoice.id] = line.amount
return res
Or even better:
.. highlight:: python
::
2008-09-29 09:44:09 +02:00
def get_total(self, cursor, user, ids, names, arg, context=None):
2008-09-26 19:18:49 +02:00
res = {'total': {}.fromkeys(ids, 0.0),
'total_service': {}.fromkeys(ids, 0.0)}
for invoice in self.browse(cursor, user, ids, context=context):
for line in invoice:
if invoice.id in res['total']:
res['total'][invoice.id] += line.amount
else:
res['total'][invoice.id] = line.amount
if line.kind != "service":
continue
if invoice.id in res['total_service']:
res['total_service'][invoice.id] += line.amount
else:
res['total_service'][invoice.id] = line.amount
return res
The framework is able to check if ``names`` (instead of ``name``) is
used in the method definition, hence adapting the way the method is
called.
2008-10-06 14:40:59 +02:00
Another way to tackle Function implementation is to pass a dictionary
to the ``args`` argument on the field definition. It will be forwarded
to the function call:
.. highlight:: python
::
state = fields.Function(
'get_state', type='selection', string='Total Service',
args={'key':'value'},
selection=[('draft','Draft'),('done','Done')],
)
def get_state(self, cursor, user, ids, names, arg, context=None):
# [...]
if arg.get('key'):
pass # do something with 'value'
2008-09-26 19:18:49 +02:00
Search on function fields
`````````````````````````
2008-12-24 11:27:23 -08:00
Another improvement would be to provide a search function.
Indeed without it the user will not be able to search across
invoices for a certain amount. If we forget about the
``total_service`` field, solution could be something like the
following:
2008-09-26 19:18:49 +02:00
.. highlight:: python
::
total = fields.Function('get_total', type='float', string='Total',
fnct_search='search_total')
def get_total(self, cursor, user, ids, name, arg, context=None):
pass #<See first example>
def search_total(self, cursor, user, name, domain=[], context=None):
# First fetch all the invoice ids
invoice_ids = self.search(cursor, user, [], context=context)
# Then collect total for each one, implicitly calling get_total:
lines = []
for invoice in self.browse(cursor, user, invoice_ids, context=context):
lines.append({'invoice': invoice.id, 'total': invoice.total})
res= [l['invoice'] for l in lines if self._eval_domain(l, domain)]
return [('id', 'in', res)]
def _eval_domain(self, line, domain):
# domain is something like: [('total', '<', 20), ('total', '>', 10)]
res = True
for field, operator, operand in domain:
value = line.get(field)
if value == None:
return False
if operator not in ("=", ">=", "<=", ">", "<", "!="):
return False
if operator == "=":
operator= "=="
res = res and (eval(str(value) + operator + str(operand)))
return res
2008-12-24 11:27:23 -08:00
One should note that such an implementation would be very slow for a large
2008-09-26 19:18:49 +02:00
number of invoices.
Write on function fields
````````````````````````
It's also possible to allow the user to write on a function field:
.. highlight:: python
::
name = fields.Function('get_name', type='char', string='Total',
fnct_inv='set_name')
hidden_name= fields.Char('Hidden')
def set_name(self, cursor, user, id, name, value, arg, context=None):
self.write(cursor, user, id, {'hidden_name': value}, context=context)
def get_name(self, cursor, user, ids, name, arg, context=None):
res = {}
for party in self.browse(cursor, user, ids, context=context):
2008-09-29 15:29:45 +02:00
res[party.id] = party.hidden_name or "unknown"
2008-09-26 19:18:49 +02:00
return res
2008-12-24 11:27:23 -08:00
This simplistic (and inefficient) example is another way to handle a default
value on the ``name`` field.
2008-09-26 00:32:22 +02:00
Fields options
^^^^^^^^^^^^^^
2008-12-24 11:27:23 -08:00
The following options are available on all type of fields, except when stated
2008-09-29 15:29:45 +02:00
otherwise in the description.
2008-09-26 00:32:22 +02:00
2008-12-24 11:27:23 -08:00
* ``readonly``: A boolean. when set to ``True`` the field is not
2008-09-26 00:32:22 +02:00
editable in the interface.
* ``required``: A boolean. When a field is required a ``NOT NULL``
2008-12-24 11:27:23 -08:00
constraint is added in the database. It will appear with a blue
2008-09-26 00:32:22 +02:00
background in the interface.
2008-12-24 11:27:23 -08:00
* ``help``: A text field shown in the interface on mouse-over.
2008-09-26 00:32:22 +02:00
2008-09-26 19:18:49 +02:00
* ``select``: An integer. When equal to ``1``, an index is
2008-12-24 11:27:23 -08:00
created in the database and the field will appear in the search box on
2008-09-26 12:04:57 +02:00
list view. When equal to ``2`` the field appear in the *Advanced
Search* part of the search box.
2008-09-26 00:32:22 +02:00
2008-10-31 21:35:34 +01:00
* ``on_change``: A list of values. If set, the client will call
2008-12-24 11:27:23 -08:00
the method ``on_change_<field_name>`` when a user changes the field.
It then passes this list of values as arguments to the function.
This method must return a dictionary ``{field_name: new_value}``
for all the field that must be updated.
2008-09-26 00:32:22 +02:00
2008-09-29 15:29:45 +02:00
* ``states``: A dictionary. Keys are name of other options and
2008-12-24 11:27:23 -08:00
values are python expression. This allows options to update
dynamically the current field. E.g.: ``states={"readonly":
2008-09-26 00:32:22 +02:00
"total > 10"}``.
2008-09-26 19:18:49 +02:00
* ``domain``: A domain on the current field. E.g.: ``[('name',
'!=', 'Steve')]`` on the ``party`` field of the
2008-12-24 11:27:23 -08:00
``relationship.address`` model will forbid linking the current
2008-09-26 19:18:49 +02:00
address to a Party for which ``name`` is equal to ``Steve``. See
:ref:`search_clause` for a more complete explanation.
2008-09-26 00:32:22 +02:00
* ``translate``: If true, this field is translatable. A flag in the
interface will allow users to change translate the field for
2008-09-26 12:04:57 +02:00
the defined language.
2008-09-26 00:32:22 +02:00
2008-12-24 11:27:23 -08:00
* ``priority``: An integer. Force the order in which fields are
written in the database. This is used only for fields
2008-09-26 00:32:22 +02:00
that are not in the table, like One2Many.
2008-12-24 11:27:23 -08:00
* ``change_default``: When the user chooses a default value for a
2008-09-26 00:32:22 +02:00
field in the current model, the current field with
``change_default`` equal to ``True`` can be used as a a condition
to the default value.
2008-09-26 12:04:57 +02:00
* ``on_change_with``: Like ``on_change``, but defined the other
way around: It's a list containing all the fields that must
2008-09-26 19:18:49 +02:00
update the current field.
2008-09-26 00:32:22 +02:00
* ``size``: A maximum size on ``Char`` fields.
2008-12-24 11:27:23 -08:00
* ``digits``: A pair of integers which define the total number of
2008-09-26 00:32:22 +02:00
digit and the number of decimal to show in the interface. Only
for ``Float`` and ``Numeric``.
* ``on_delete``: Sql expression handling behaviour when a the
target of a ``Many2One`` is removed. Possible values:
2008-09-26 19:18:49 +02:00
``CASCADE``, ``NO ACTION``, ``RESTRICT``, ``SET DEFAULT``, ``SET
2008-09-26 12:04:57 +02:00
NULL`` (default).
2008-09-26 00:32:22 +02:00
2008-12-24 11:27:23 -08:00
* ``context``: A string defining a dictionary which will be given
2008-09-26 12:04:57 +02:00
to evaluate the relation fields.
2008-09-26 00:32:22 +02:00
* ``ondelete_origin`` and ``ondelete_target``: Like ``on_delete``
2008-09-26 19:18:49 +02:00
for the columns of the table supporting a ``Many2Many`` relation.
2008-09-24 19:03:48 +02:00
Manipulating Models
2008-11-04 19:11:39 +01:00
*******************
2008-09-24 19:03:48 +02:00
2008-09-29 14:07:04 +02:00
.. module:: OSV
2008-09-24 19:03:48 +02:00
Create
2008-11-04 19:11:39 +01:00
^^^^^^
2008-09-24 19:03:48 +02:00
2008-09-29 14:07:04 +02:00
.. method:: create(self, cursor, user, vals[, context])
2008-09-26 00:32:22 +02:00
2008-09-29 14:07:04 +02:00
:param cursor: An instance of the ``Fakecursor`` class.
2008-09-26 00:32:22 +02:00
2008-09-29 14:07:04 +02:00
:param user: The id of the user initiating the action.
2008-09-26 00:32:22 +02:00
2008-09-29 15:29:45 +02:00
:param vals: A dictionary containing the values to be written in
2008-09-29 14:07:04 +02:00
the database.
2008-09-26 00:32:22 +02:00
2008-09-29 14:07:04 +02:00
:param context: The context of the action.
2008-09-26 00:32:22 +02:00
2008-09-29 14:07:04 +02:00
:return: An integer, the id of the new record.
2008-09-26 00:32:22 +02:00
2008-09-24 19:03:48 +02:00
Read
2008-11-04 19:11:39 +01:00
^^^^
2008-09-24 19:03:48 +02:00
2008-09-29 14:07:04 +02:00
.. method:: read(self, cursor, user, ids[, fields_names, context])
2008-09-26 00:32:22 +02:00
2008-09-29 14:07:04 +02:00
:param cursor: An instance of the ``Fakecursor`` class.
2008-09-26 00:32:22 +02:00
2008-09-29 14:07:04 +02:00
:param user: The id of the user initiating the action.
2008-09-26 00:32:22 +02:00
2008-09-29 14:07:04 +02:00
:param ids: A list of integer defining the rows to be read.
2008-09-26 00:32:22 +02:00
2008-09-29 14:07:04 +02:00
:param fields_name: A list of the name of the columns to be
2008-09-26 00:32:22 +02:00
read. If empty all the columns a read.
2008-09-29 14:07:04 +02:00
:param context: The context of the action.
2008-09-29 15:29:45 +02:00
:return: A list of dictionary whose keys are the fields names.
2008-09-29 15:29:45 +02:00
Note: one should favour ``browse`` over ``read``, because it's more
2008-09-26 00:32:22 +02:00
powerful.
2008-09-25 00:53:40 +02:00
Browse
2008-11-04 19:11:39 +01:00
^^^^^^
2008-09-25 00:53:40 +02:00
2008-09-29 14:07:04 +02:00
.. method:: browse(self, cursor, user, ids[, context])
2008-09-26 00:32:22 +02:00
2008-09-29 14:07:04 +02:00
:param cursor: An instance of the ``Fakecursor`` class.
2008-09-26 00:32:22 +02:00
2008-09-29 14:07:04 +02:00
:param user: The id of the user initiating the action.
2008-09-26 00:32:22 +02:00
2008-09-29 14:07:04 +02:00
:param ids: A list of integer defining the rows to be read.
2008-09-26 00:32:22 +02:00
2008-09-29 14:07:04 +02:00
:param context: The context of the action.
2008-09-26 00:32:22 +02:00
2008-09-29 14:07:04 +02:00
:return: A ``BrowseRecordList`` instance.
2008-09-26 00:32:22 +02:00
Example usage:
.. highlight:: python
::
party_obj = self.pool.get('relationship.party')
parties = party_obj.browse(self, cursor, user, ids, context=None)
countries = Set()
for party in parties:
for address in party.addresses:
countries.add(party.country.name)
This example collect all the countries connected to a given set of
parties (defined by ``ids``).
One can see that the ``BrowseRecord`` list return by the ``browse`` function
is able to resolve foreign keys by itself and thus allowing to browse
the data in a pythonic way.
2008-09-26 19:18:49 +02:00
2008-09-24 19:03:48 +02:00
Write
2008-11-04 19:11:39 +01:00
^^^^^
2008-09-24 19:03:48 +02:00
2008-09-29 14:07:04 +02:00
.. method:: write(self, cursor, user, ids, vals[, context])
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param cursor: An instance of the ``Fakecursor`` class.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param user: The id of the user initiating the action.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param ids: A list of integer defining the rows to be written.
2008-09-26 19:18:49 +02:00
2008-09-29 15:29:45 +02:00
:param vals: A dictionary containing the values to be written in the
2008-09-26 19:18:49 +02:00
database.
2008-09-29 14:07:04 +02:00
:param context: The context of the action.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:return: ``True``
2008-09-26 19:18:49 +02:00
2008-09-24 19:03:48 +02:00
Delete
2008-11-04 19:11:39 +01:00
^^^^^^
2008-09-24 19:03:48 +02:00
2008-09-29 14:07:04 +02:00
.. method:: delete(self, cursor, user, ids[, context])
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param self: The current model on which the action take place.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param cursor: An instance of the ``Fakecursor`` class.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param user: The id of the user initiating the action.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param ids: A list of integer defining the rows to be deleted.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param context: The context of the action.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:return: ``True``
2008-09-26 19:18:49 +02:00
2008-09-26 00:32:22 +02:00
Search
2008-11-04 19:11:39 +01:00
^^^^^^
2008-09-26 00:32:22 +02:00
2008-09-29 14:07:04 +02:00
.. method:: search(self, cursor, user, args[, offset, limit, order,context, count, query_string])
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param self: The current model on which the action take place.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param cursor: An instance of the ``Fakecursor`` class.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param args: The search clause, see :ref:`search_clause` for
details.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param offset: An integer. Specify the offset in the results.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param limit: An integer. The maximum number of results.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param order: A list of tuple. The first element of each tuple is a
name of the field, the second is ``ASC`` or
``DESC``. E.g.: ``[('date', 'DESC'),('name',
'ASC')]``.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param context: The context of the action.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:param count: A boolean. If true, the result is the length of all
2008-09-26 19:18:49 +02:00
the items found.
2008-09-29 14:07:04 +02:00
:param query_string: A boolean: If true, the result is a tuple with
the generated sql query and his arguments.
2008-09-26 19:18:49 +02:00
2008-09-29 14:07:04 +02:00
:return: A list of ids.
2008-09-26 19:18:49 +02:00
.. _search_clause:
Search clauses
^^^^^^^^^^^^^^
Simple clause are a list of condition, with an implicit ``AND``
operator:
.. highlight:: python
::
[('name', '=', 'Bob'),('age','>=', 20)]
More complex clause can be made this way:
.. highlight:: python
::
2008-09-29 15:29:45 +02:00
[ 'OR', [('name', '=', 'Bob'),('city','in', ['Brussels', 'Paris'])],
2008-09-26 19:18:49 +02:00
[('name', '=', 'Charlie'),('country.name','=', 'Belgium')],
]
Where ``country`` is a ``Many2One`` field on the current field. The
number *dots* in the left hand side of a condition is not limited, but
the underlying relation must be a ``Many2One``.
Which if used in a search call on the Address model will result in
something similar to the following sql code (the actual sql query will
2008-09-29 15:29:45 +02:00
be more complex since it has to take care of the access rights of the
2008-09-26 19:18:49 +02:00
user.):
.. highlight:: sql
::
SELECT relationship_address.id FROM relationship_address
2008-09-29 15:29:45 +02:00
JOIN relationship_country ON
2008-09-26 19:18:49 +02:00
(relationship_address.country = relationship_country.id)
WHERE (relationship_address.name = 'Bob' AND
2008-09-29 15:29:45 +02:00
relationship_address.city in ('Brussels', 'Paris'))
2008-09-26 19:18:49 +02:00
OR
(relationship_address.name = 'Charlie' AND
relationship_country.name = 'Belgium')
.. _define_inheritance:
2008-09-26 19:18:49 +02:00
Models Inheritance
2008-11-04 19:11:39 +01:00
******************
2008-09-26 19:18:49 +02:00
2008-09-29 15:00:00 +02:00
Model Inheritance allow add or override fields, methods and
constraints on existing models. To inherit an existing model (like
``Party`` on the first example), one just need to instantiate a class
2008-09-29 15:29:45 +02:00
with the same ``_name``:
2008-09-25 00:53:40 +02:00
2008-09-29 15:00:00 +02:00
.. highlight:: python
::
class Car(OSV):
_name = "vehicle.car"
_rec_name = model
model = fields.Char("Model", required=True)
manufacturer = fields.Char("Manufacturer")
first_owner = fields.Many2One('relationship.party', 'First Owner')
Car()
class Party(OSV):
_name = "relationship.party"
current_car = fields.Many2One('vehicle.car', 'Current car')
def __init__(self):
super(Party, self).__init__()
self._sql_constraints += [
('party_car_uniq', 'UNIQUE(model)',
'Two party cannot use the same car!'),
]
2008-09-29 15:00:00 +02:00
Party()
This show how to define a new model and link an existing one to it.
This is also a way to define reflecting ``Many2One``: It's not
possible to create the two models without using inheritance because
2008-09-29 15:29:45 +02:00
each of the foreign key (``first_owner`` and ``current_car``) need the
2008-09-29 15:00:00 +02:00
other model table.