After creating a new model, the next step is to add fields to it. Odoo supports all the basic data types expected, such as text strings, integers, floating point numbers, Booleans, dates, datetimes, and image/binary data.
Some field names are special, wither because they are reserved by the ORM for special purposes, or because some built-in features by default use some default field names.
Let's explore the several types of fields available in Odoo.
We now have a Stage
model and we will expand it to add some additional fields. We should edit the todo_ui/models/todo_model.py
file and add extra field definitions to make it look like this:
class Stage(models.Model): _name = 'todo.task.stage' _description = 'To-do Stage' _order = 'sequence,name' # String fields: name = fields.Char('Name', 40) desc = fields.Text('Description') state = fields.Selection( [('draft','New'), ('open','Started'), ('done','Closed')],'State') docs = fields.Html('Documentation') # Numeric fields: sequence = fields.Integer('Sequence') perc_complete = fields.Float('% Complete', (3, 2)) # Date fields: date_effective = fields.Date('Effective Date') date_changed = fields.Datetime('Last Changed') # Other fields: fold = fields.Boolean('Folded?') image = fields.Binary('Image')
Here, we have a sample of the non-relational field types available in Odoo with the positional arguments expected by each one.
In most cases, the first argument is the field title, corresponding to the string
field argument; this is used as the default text for the user interface labels. It's optional, and if not provided, a title will be automatically generated from the field name.
For date field names, there is a convention to use date as a prefix. For example, we should use date_effective
field instead of effective_date
. Similar conventions also apply to other fields, such as amount_
, price_
, or qty_
.
These are the standard positional arguments expected by each of the field types:
Char
expects a second, optional, argument size, for the maximum text size. It's recommended to not use it unless there is business requirement that requires it, such as a social security number with a fixed length.Text
differs from Char
, in that, it can hold multiline text content, but expects the same arguments.Selection
is a drop-down selection list. The first argument is the list of selectable options and the second is the string title. The selection item is a list of ('value', 'Title')
tuples, for the value stored in the database and the corresponding user interface description. When extending through inheritance, the selection_add
argument is available to append new items to an existing selection list.Html
is stored as a text field, but has specific handling on the user interface, for HTML content presentation. For security reasons, they are sanitized by default, but this behavior can be overridden.Integer
just expects a string argument for the field title.Float
has a second optional argument, an (x,y)
tuple with the field's precision: x
is the total number of digits; of those, y
are decimal digits.Date
and Datetime
fields expect only the string text as a positional argument. For historical reasons, the ORM handles their values in a string format. Helper functions should be used to convert them to actual date objects. Also the datetime values are stored in the database in UTC time but presented in local time, using the user's time zone preferences. This is discussed in more detail in Chapter 6, Views - Designing the User Interface.Boolean
holds True
or False
values, as you might expect, and only have one positional argument for the string text.Binary
stores file-like binary data, and also expects only the string argument. They can be handled by Python code using base64
encoded strings.Other than these, we also have the relational fields, which will be introduced later in this chapter. But now, there is still more to learn about these field types and their attributes.
Fields have attributes that can be set when defining them. Depending on the field type, a few attributes can be passed positionally, without an argument keyword, as shown in the previous section.
For example, name=fields.Char('Name', 40)
could make use of positional arguments. Using the keyword arguments, the same could be written as name=fields.Char(size=40, string='Name')
. More information on keyword arguments can be found in the Python official documentation at
https://docs.python.org/2/tutorial/controlflow.html#keyword-arguments
.
All the available attributes can be passed as a keyword argument. These are the generally available attributes and the corresponding argument keywords:
string
is the field default label, to be used in the user interface. Except for selection and relational fields, it is the first positional argument, so most of the time it is not used as a keyword argument.default
sets a default value for the field. It can be a static value, such as a string, or a callable reference, either a named function or an anonymous function (a lambda expression).size
applies only to Char
fields, and can set a maximum size allowed. Current best practice is to not use it unless it's really needed.translate
applies only to Char
, Text
, and Html
fields, and makes the field contents translatable, holding different values for different languages.help
provides the text for tooltips displayed to the users.readonly=True
makes the field by default not editable on the user interface. This is not enforced at the API level; it is only a user interface setting.required=True
makes the field by default mandatory in the user interface. This is enforced at the database level by adding a NOT NULL
constraint on the column.index=True
will create a database index on the field.copy=False
has the field ignored when using the duplicate record feature, copy()
ORM method. The non-relational fields are copyable
by default.groups
allows limiting the field's access and visibility to only some groups. It expects a comma separated list of XML IDs for security groups, such as groups='base.group_user,base.group_system'
.states
expects a dictionary mapping values for UI attributes depending on values of the state
field. For example: states={'done':[('readonly',True)]}
. Attributes that can be used are readonly
, required
, and invisible
.For completeness, two other attributes are sometimes used when upgrading between Odoo major versions:
deprecated=True
logs a warning whenever the field is being used.oldname='field'
is used when a field is renamed in a newer version, enabling the data in the old field to be automatically copied into the new field.A few field names are reserved to be used by the ORM.
The id
field is an automatic number uniquely identifying each record, and used as the database primary key. It's automatically added to every model.
The following fields are automatically created on new models, unless the _log_access=False
model attribute is set:
create_uid
is for the user that created the recordcreate_date
is for the date and time when the record is createdwrite_uid
is for the last user to modify the recordwrite_date
is for the last date and time when the record was modifiedThis information is available from the web client, navigating to the Developer Mode menu and selecting the View Metadata option.
Some API built-in features by default expect specific field names. We should avoid using these field names for purposes other than the intended ones. Some of them are even reserved and can't be used for other purposes at all:
name
is used by default as the display name for the record. Usually it is a Char
, but can also be a Text
or a Many2one
field type. We can still set another field to be used for display name, using the _rec_name
model attribute.Active
, of type Boolean
, allows inactivating records. Records with active==False
will automatically be excluded from queries. To access them an ('active','=',False)
condition must be added to the search domain, or 'active_test': False
should be added to the current context.Sequence
, of type Integer
, if present in a list view, allows to manually define the order of the records. To work properly you should not forget to use it with the model's _order
attribute.State
, of type Selection
, represents basic states of the record's life cycle, and can be used by the state's field attribute to dynamically modify the view: some form fields can be made readonly
, required
, or invisible
in specific record states.parent_id
, parent_left
, and parent_right
, of type Integer
, have special meaning for parent/child hierarchical relations. We will discuss them in detail in the next section.So far, we've discussed non-relational fields. But a good part of an application data structure is about describing the relationships between entities. Let's look at that now.