documentation/content/developer/howtos/rdtraining/04_basicmodel.rst
Victor Feyens d0c2cb17bc [ADD] developer/howtos: R&D Training
The new R&D training is intended to replace the existing technical
training(s). It is organized as follow:

- A core training, with chapters to follow in order (1 - 16)
- Advanced topics, with independent chapters (A - O)

The advanced topics should be done after the core training.

Co-authored-by: Nicolas Martinelli <nim@odoo.com>
Co-authored-by: Jorge Pinna Puissant <jpp@odoo.com>
Co-authored-by: wan <wan@odoo.com>
Co-authored-by: Xavier Morel <xmo@odoo.com>
Co-authored-by: Tiffany Chang (tic) <tic@odoo.com>
2021-05-18 15:24:16 +02:00

299 lines
12 KiB
ReStructuredText

.. _howto/rdtraining/04_basicmodel:
==================================
Chapter 4: Models And Basic Fields
==================================
At the end of the :ref:`previous chapter <howto/rdtraining/03_newapp>`, we were able to create an Odoo
module. However, at this point it is still an empty shell which doesn't allow us to store any data.
In our real estate module, we want to store the information related to the properties
(name, description, price, living area...) in a database. The Odoo framework provides tools to
facilitate database interactions.
Before moving forward in the exercise, make sure the ``estate`` module is installed, i.e. it
must appear as 'Installed' in the Apps list.
Object-Relational Mapping
=========================
**Reference**: the documentation related to this topic can be found in the
:ref:`reference/orm/model` API.
.. note::
**Goal**: at the end of this section, the table ``estate_property`` should be created:
.. code-block:: text
$ psql -d rd-demo
rd-demo=# SELECT COUNT(*) FROM estate_property;
count
-------
0
(1 row)
A key component of Odoo is the `ORM`_ layer.
This layer avoids having to manually write most `SQL`_
and provides extensibility and security services\ [#rawsql]_.
Business objects are declared as Python classes extending
:class:`~odoo.models.Model`, which integrates them into the automated
persistence system.
Models can be configured by setting attributes in their
definition. The most important attribute is
:attr:`~odoo.models.Model._name`, which is required and defines the name for
the model in the Odoo system. Here is a minimum definition of a
model::
from odoo import models
class TestModel(models.Model):
_name = "test.model"
This definition is enough for the ORM to generate a database table named ``test_model``. The
``.`` in the model ``_name`` is automatically converted into a ``_`` by the ORM. By convention all
models are located in a ``models`` directory and each model is defined in its own Python
file.
Take a look at how the ``crm_recurring_plan`` table is defined and how the corresponding Python
file is imported:
1. The model is defined in the file ``crm/models/crm_recurring_plan.py``
(see `here <https://github.com/odoo/odoo/blob/e80911aaead031e7523173789e946ac1fd27c7dc/addons/crm/models/crm_recurring_plan.py#L1-L9>`__)
2. The file ``crm_recurring_plan.py`` is imported in ``crm/models/__init__.py``
(see `here <https://github.com/odoo/odoo/blob/e80911aaead031e7523173789e946ac1fd27c7dc/addons/crm/models/__init__.py#L15>`__)
3. The folder ``models`` is imported in ``crm/__init__.py``
(see `here <https://github.com/odoo/odoo/blob/e80911aaead031e7523173789e946ac1fd27c7dc/addons/crm/__init__.py#L5>`__)
.. exercise:: Define the real estate properties model.
Based on example given in the CRM module, create the appropriate files and folder for the
``estate_property`` table.
When the files are created, add a minimum definition for the
``estate.property`` model.
Any modification of the Python files requires a restart of the Odoo server. When we restart
the server, we will add the parameters ``-d`` and ``-u``:
.. code-block:: console
$ ./odoo-bin --addons-path=../custom,../enterprise/,addons -d rd-demo -u estate
``-u estate`` means we want to upgrade the ``estate`` module, i.e. the ORM will
apply database schema changes. In this case it creates a new table. ``-d rd-demo`` means
that the upgrade should be performed on the ``rd-demo`` database. ``-u`` should always be used in
combination with ``-d``.
During the startup you should see the following warnings:
.. code-block:: text
...
WARNING rd-demo odoo.models: The model estate.property has no _description
...
WARNING rd-demo odoo.modules.loading: The model estate.property has no access rules, consider adding one...
...
If this is the case, then you should be good! To be sure, double check with ``psql`` as demonstrated in
the **Goal**.
.. exercise:: Add a description.
Add a ``_description`` to your model to get rid of one of the warnings.
Model fields
============
**Reference**: the documentation related to this topic can be found in the
:ref:`reference/orm/fields` API.
Fields are used to define what the model can store and where they are stored. Fields are
defined as attributes in the model class::
from odoo import fields, models
class TestModel(models.Model):
_name = "test.model"
_description = "Test Model"
name = fields.Char()
The ``name`` field is a :class:`~odoo.fields.Char` which will be represented as a Python
unicode ``str`` and a SQL ``VARCHAR``.
Types
-----
.. note::
**Goal**: at the end of this section, several basic fields should have been added to the table
``estate_property``:
.. code-block:: text
$ psql -d rd-demo
rd-demo=# \d estate_property;
Table "public.estate_property"
Column | Type | Collation | Nullable | Default
--------------------+-----------------------------+-----------+----------+---------------------------------------------
id | integer | | not null | nextval('estate_property_id_seq'::regclass)
create_uid | integer | | |
create_date | timestamp without time zone | | |
write_uid | integer | | |
write_date | timestamp without time zone | | |
name | character varying | | |
description | text | | |
postcode | character varying | | |
date_availability | date | | |
expected_price | double precision | | |
selling_price | double precision | | |
bedrooms | integer | | |
living_area | integer | | |
facades | integer | | |
garage | boolean | | |
garden | boolean | | |
garden_area | integer | | |
garden_orientation | character varying | | |
Indexes:
"estate_property_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"estate_property_create_uid_fkey" FOREIGN KEY (create_uid) REFERENCES res_users(id) ON DELETE SET NULL
"estate_property_write_uid_fkey" FOREIGN KEY (write_uid) REFERENCES res_users(id) ON DELETE SET NULL
There are two broad categories of fields: 'simple' fields, which are atomic
values stored directly in the model's table, and 'relational' fields, which link
records (of the same or different models).
Simple field examples are :class:`~odoo.fields.Boolean`, :class:`~odoo.fields.Float`,
:class:`~odoo.fields.Char`, :class:`~odoo.fields.Text`, :class:`~odoo.fields.Date`
and :class:`~odoo.fields.Selection`.
.. exercise:: Add basic fields to the Real Estate Property table.
Add the following basic fields to the table:
========================= =========================
Field Type
========================= =========================
name Char
description Text
postcode Char
date_availability Date
expected_price Float
selling_price Float
bedrooms Integer
living_area Integer
facades Integer
garage Boolean
garden Boolean
garden_area Integer
garden_orientation Selection
========================= =========================
The ``garden_orientation`` field must have 4 possible values: 'North', 'South', 'East'
and 'West'. The selection list is defined as a list of tuples, see
`here <https://github.com/odoo/odoo/blob/b0e0035b585f976e912e97e7f95f66b525bc8e43/addons/crm/report/crm_activity_report.py#L31-L34>`__
for an example.
When the fields are added to the model, restart the server with ``-u estate``
.. code-block:: console
$ ./odoo-bin --addons-path=../custom,../enterprise/,addons -d rd-demo -u estate
Connect to ``psql`` and check the structure of the table ``estate_property``. You'll notice that
a couple of extra fields were also added to the table. We will revisit them later.
Common Attributes
-----------------
.. note::
**Goal**: at the end of this section, the columns ``name`` and ``expected_price`` should be
not nullable in the table ``estate_property``:
.. code-block:: console
rd-demo=# \d estate_property;
Table "public.estate_property"
Column | Type | Collation | Nullable | Default
--------------------+-----------------------------+-----------+----------+---------------------------------------------
...
name | character varying | | not null |
...
expected_price | double precision | | not null |
...
Much like the model itself, fields can be configured by passing
configuration attributes as parameters::
name = field.Char(required=True)
Some attributes are available on all fields, here are the most common ones:
:attr:`~odoo.fields.Field.string` (``str``, default: field's name)
The label of the field in UI (visible by users).
:attr:`~odoo.fields.Field.required` (``bool``, default: ``False``)
If ``True``, the field can not be empty. It must either have a default
value or always be given a value when creating a record.
:attr:`~odoo.fields.Field.help` (``str``, default: ``''``)
Provides long-form help tooltip for users in the UI.
:attr:`~odoo.fields.Field.index` (``bool``, default: ``False``)
Requests that Odoo create a `database index`_ on the column.
.. exercise:: Set attributes for existing fields.
Add the following attributes:
========================= =========================
Field Attribute
========================= =========================
name required
expected_price required
========================= =========================
After restarting the server, both fields should be not nullable.
Automatic Fields
----------------
**Reference**: the documentation related to this topic can be found in
:ref:`reference/fields/automatic`.
You may have noticed your model has a few fields you never defined.
Odoo creates a few fields in all models\ [#autofields]_. These fields are
managed by the system and can't be written to, but they can be read if
useful or necessary:
:attr:`~odoo.fields.Model.id` (:class:`~odoo.fields.Id`)
The unique identifier for a record of the model.
:attr:`~odoo.fields.Model.create_date` (:class:`~odoo.fields.Datetime`)
Creation date of the record.
:attr:`~odoo.fields.Model.create_uid` (:class:`~odoo.fields.Many2one`)
User who created the record.
:attr:`~odoo.fields.Model.write_date` (:class:`~odoo.fields.Datetime`)
Last modification date of the record.
:attr:`~odoo.fields.Model.write_uid` (:class:`~odoo.fields.Many2one`)
User who last modified the record.
Now that we have created our first model, let's
:ref:`add some security <howto/rdtraining/05_securityintro>`!
.. [#autofields] it is possible to :ref:`disable the automatic creation of some
fields <reference/fields/automatic/log_access>`
.. [#rawsql] writing raw SQL queries is possible, but requires caution as this
bypasses all Odoo authentication and security mechanisms.
.. _database index:
https://use-the-index-luke.com/sql/preface
.. _ORM:
https://en.wikipedia.org/wiki/Object-relational_mapping
.. _SQL:
https://en.wikipedia.org/wiki/SQL