chapter 5 - connect the dots
This commit is contained in:
parent
32cba451c1
commit
81929ceb1c
@ -813,6 +813,8 @@ In general in Odoo, when manipulating strings, prefer ``%`` over ``.format()``
|
||||
of position (when multiple variables have to be replaced). This makes the
|
||||
translation easier for the community translators.
|
||||
|
||||
.. _contributing/coding_guidelines/model_members:
|
||||
|
||||
Symbols and Conventions
|
||||
-----------------------
|
||||
|
||||
|
@ -518,6 +518,8 @@ behavior is desired:
|
||||
:class:`~odoo.fields.Many2one`
|
||||
:type: :class:`~odoo.addons.base.models.res_company`
|
||||
|
||||
.. _reference/orm/recordsets:
|
||||
|
||||
Recordsets
|
||||
==========
|
||||
|
||||
|
@ -39,7 +39,7 @@ Ready? Let's get started!
|
||||
- :doc:`server_framework_101/02_lay_the_foundations`
|
||||
- :doc:`server_framework_101/03_build_user_interface`
|
||||
- :doc:`server_framework_101/04_relational_fields`
|
||||
- :doc:`server_framework_101/05_business_logic`
|
||||
- :doc:`server_framework_101/05_connect_the_dots`
|
||||
- :doc:`server_framework_101/06_security`
|
||||
- :doc:`server_framework_101/07_advanced_views`
|
||||
- :doc:`server_framework_101/08_inheritance`
|
||||
|
@ -65,8 +65,8 @@ classes, fields are defined as class attributes. Each field is an instance of a
|
||||
`odoo.fields` package. For example, `Char`, `Float`, `Boolean`, each designed to handle different
|
||||
types of data. When defining a field, developers can pass various arguments to finely control how
|
||||
data is handled and presented in Odoo. For example, `string` defines the label for the field in the
|
||||
user interface, `help` provides a tooltip when hovering the field in the user interface, and
|
||||
`required` makes filling in the field mandatory.
|
||||
user interface, `help` provides a tooltip when hovering the field in the user interface, `required`
|
||||
makes filling in the field mandatory, and `default` provides a default field value.
|
||||
|
||||
Individual data entries are called **records**. They are based on the structure defined by models
|
||||
and contain the actual data for each field specified in the model. In Python, records are
|
||||
@ -74,10 +74,6 @@ represented as instances of the model's class, allowing developers to interact w
|
||||
object-oriented programming techniques. For example, in a real estate application using a tenant
|
||||
model, each specific tenant (such as "Bafien Carpink") would be a separate record of that model.
|
||||
|
||||
.. seealso::
|
||||
For the full list of fields and their attributes, see the :ref:`reference documentation
|
||||
<reference/orm/fields>`.
|
||||
|
||||
.. example::
|
||||
Before we dive into creating our own models, let's take a look at a basic example of a model that
|
||||
represents storable products. It defines a `product` model with the `Product` class inheriting
|
||||
@ -94,12 +90,12 @@ model, each specific tenant (such as "Bafien Carpink") would be a separate recor
|
||||
|
||||
name = fields.Char(string="Name", required=True)
|
||||
description = fields.Text(string="Description")
|
||||
price = fields.Float(string="Sale Price", required=True)
|
||||
price = fields.Float(string="Sales Price", required=True)
|
||||
category = fields.Selection(
|
||||
string="Category",
|
||||
help="The category of the product; if none are suitable, select 'Other'.",
|
||||
selection=[
|
||||
('apparel', "Clothing")
|
||||
('apparel', "Clothing"),
|
||||
('electronics', "Electronics"),
|
||||
('home_decor', "Home Decor"),
|
||||
('other', "Other"),
|
||||
@ -116,11 +112,14 @@ model, each specific tenant (such as "Bafien Carpink") would be a separate recor
|
||||
- `category` is a `Selection` field with predefined options, each defined by a technical code
|
||||
and a corresponding label. Since it is required, a default value is provided.
|
||||
|
||||
.. seealso::
|
||||
For the full list of fields and their attributes, see the :ref:`reference documentation
|
||||
<reference/orm/fields>`.
|
||||
|
||||
Building on these new concepts, let's now create the first model for our real estate app. We'll
|
||||
create a model with some fields to represent real estate properties and their characteristics.
|
||||
|
||||
.. exercise::
|
||||
|
||||
#. Create a new :file:`real_estate_property.py` file at the root of the `real_estate` module.
|
||||
#. Update the :file:`real_estate/__init__.py` file to relatively import the
|
||||
:file:`real_estate_property.py` file, like so:
|
||||
@ -132,19 +131,21 @@ create a model with some fields to represent real estate properties and their ch
|
||||
#. Define a new model with `real.estate.property` as `_name` and a short `_description`.
|
||||
#. Add fields to represent the following characteristics:
|
||||
|
||||
- Name (required)
|
||||
- Description
|
||||
- Image (max 600x400 pixels)
|
||||
- Active (default to true)
|
||||
- State (new, offer received, under option, or sold; required; default to new)
|
||||
- Type (house, apartment, office building, retail space, or warehouse; required; default to
|
||||
house)
|
||||
- Selling Price (without currency; with help text; required)
|
||||
- Availability Date (default to creation date + two months)
|
||||
- Floor Area (in square meters; with help text)
|
||||
- Number of Bedrooms (default to two)
|
||||
- Whether there is a garden
|
||||
- Whether there is a garage
|
||||
- :guilabel:`Name` (required)
|
||||
- :guilabel:`Description`
|
||||
- :guilabel:`Image` (max 600x400 pixels)
|
||||
- :guilabel:`Active` (whether the property listing is active; defaults to true)
|
||||
- :guilabel:`State` (:guilabel:`New`, :guilabel:`Offer Received`, :guilabel:`Under Option`, or
|
||||
:guilabel:`Sold`; required; defaults to :guilabel:`New`)
|
||||
- :guilabel:`Type` (:guilabel:`House`, :guilabel:`Apartment`, :guilabel:`Office Building`,
|
||||
:guilabel:`Retail Space`, or :guilabel:`Warehouse`; required; defaults to :guilabel:`House`)
|
||||
- :guilabel:`Selling Price` (without currency; with help text; required)
|
||||
- :guilabel:`Availability Date`
|
||||
- :guilabel:`Floor Area` (in square meters; with help text)
|
||||
- :guilabel:`Number of Bedrooms` (defaults to two)
|
||||
- :guilabel:`Garage` (whether there is a garage)
|
||||
- :guilabel:`Garden` (whether there is a garden)
|
||||
- :guilabel:`Garden Area` (in square meters; with help text)
|
||||
|
||||
.. tip::
|
||||
- The class name doesn't matter, but the convention is to use the model's upper-cased `_name`
|
||||
@ -163,7 +164,6 @@ create a model with some fields to represent real estate properties and their ch
|
||||
:caption: `real_estate_property.py`
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo.tools import date_utils
|
||||
|
||||
|
||||
class RealEstateProperty(models.Model):
|
||||
@ -200,15 +200,16 @@ create a model with some fields to represent real estate properties and their ch
|
||||
selling_price = fields.Float(
|
||||
string="Selling Price", help="The selling price excluding taxes.", required=True
|
||||
)
|
||||
availability_date = fields.Date(
|
||||
string="Availability Date", default=date_utils.add(fields.Date.today(), months=2)
|
||||
)
|
||||
availability_date = fields.Date(string="Availability Date")
|
||||
floor_area = fields.Integer(
|
||||
string="Floor Area", help="The floor area in square meters excluding the garden."
|
||||
)
|
||||
bedrooms = fields.Integer(string="Number of bedrooms", default=2)
|
||||
has_garden = fields.Boolean(string="Garden")
|
||||
has_garage = fields.Boolean(string="Garage")
|
||||
has_garden = fields.Boolean(string="Garden")
|
||||
garden_area = fields.Integer(
|
||||
string="Garden Area", help="The garden area excluding the building."
|
||||
)
|
||||
|
||||
Congrats, you have just defined the first model of our real estate app! However, the changes have
|
||||
not yet been applied to the database. To do so, you must add the `-u real_estate` argument to the
|
||||
@ -235,7 +236,6 @@ you created translate into a new SQL table. We will use `psql`, the CLI
|
||||
:dfn:`command-line interface` allowing to browse and interact with PostgreSQL databases.
|
||||
|
||||
.. exercise::
|
||||
|
||||
#. In your terminal, execute the command :command:`psql -d tutorials`.
|
||||
#. Enter the command :command:`\\d real_estate_property` to print the description of the
|
||||
`real_estate_property` table.
|
||||
@ -246,6 +246,7 @@ you created translate into a new SQL table. We will use `psql`, the CLI
|
||||
.. spoiler:: Solution
|
||||
|
||||
.. code-block:: text
|
||||
:caption: terminal
|
||||
|
||||
$ psql -d tutorials
|
||||
|
||||
@ -256,6 +257,7 @@ you created translate into a new SQL table. We will use `psql`, the CLI
|
||||
id | integer | | not null | nextval('real_estate_property_id_seq'::regclass)
|
||||
floor_area | integer | | |
|
||||
bedrooms | integer | | |
|
||||
garden_area | integer | | |
|
||||
create_uid | integer | | |
|
||||
write_uid | integer | | |
|
||||
name | character varying | | not null |
|
||||
@ -264,8 +266,8 @@ you created translate into a new SQL table. We will use `psql`, the CLI
|
||||
availability_date | date | | |
|
||||
description | text | | |
|
||||
active | boolean | | |
|
||||
has_garden | boolean | | |
|
||||
has_garage | boolean | | |
|
||||
has_garden | boolean | | |
|
||||
create_date | timestamp without time zone | | |
|
||||
write_date | timestamp without time zone | | |
|
||||
selling_price | double precision | | not null |
|
||||
@ -343,9 +345,6 @@ The most common data operation is creating new records through the `record` and
|
||||
elements, but other operations exist, such as `delete`, which deletes previously created records, or
|
||||
even `function`, which allows executing arbitrary code.
|
||||
|
||||
.. seealso::
|
||||
:doc:`Reference documentation for XML data files <../../reference/backend/data>`
|
||||
|
||||
Some data operations require their data elements to be uniquely identified by the system. This is
|
||||
achieved by means of the `id` attribute, also known as the **XML ID** or **external identifier**. It
|
||||
provides a way for other elements to reference it with the `ref` attribute and links data elements
|
||||
@ -387,10 +386,12 @@ created from a data file so that records can be referenced by their full XML ID
|
||||
- The `ref` attribute is used to reference other records by their XML ID and use their record
|
||||
ID as value.
|
||||
|
||||
.. seealso::
|
||||
:doc:`Reference documentation for XML data files <../../reference/backend/data>`
|
||||
|
||||
Let's now load some default real estate properties in our database.
|
||||
|
||||
.. exercise::
|
||||
|
||||
#. Create a new :file:`real_estate_property_data.xml` file at the root of the `real_estate`
|
||||
module.
|
||||
#. Update the manifest to let the server know that it should load our data file. To do so, have
|
||||
@ -441,10 +442,12 @@ Let's now load some default real estate properties in our database.
|
||||
<field name="image" type="base64" file="real_estate/country_house.png"/>
|
||||
<field name="type">house</field>
|
||||
<field name="selling_price">745000</field>
|
||||
<field name="availability_date">2024-08-01</field>
|
||||
<field name="floor_area">416</field>
|
||||
<field name="bedrooms">5</field>
|
||||
<field name="has_garden">True</field>
|
||||
<field name="has_garage">True</field>
|
||||
<field name="has_garden">True</field>
|
||||
<field name="garden_area">2100</field>
|
||||
</record>
|
||||
|
||||
<record id="real_estate.loft" model="real.estate.property">
|
||||
@ -456,8 +459,8 @@ Let's now load some default real estate properties in our database.
|
||||
<field name="availability_date">2025-01-01</field>
|
||||
<field name="floor_area">195</field>
|
||||
<field name="bedrooms">3</field>
|
||||
<field name="has_garden">False</field>
|
||||
<field name="has_garage">True</field>
|
||||
<field name="has_garden">False</field>
|
||||
</record>
|
||||
|
||||
<record id="real_estate.mixed_use_commercial" model="real.estate.property">
|
||||
@ -469,8 +472,8 @@ Let's now load some default real estate properties in our database.
|
||||
<field name="availability_date">2024-10-02</field>
|
||||
<field name="floor_area">370</field>
|
||||
<field name="bedrooms">0</field>
|
||||
<field name="has_garden">False</field>
|
||||
<field name="has_garage">False</field>
|
||||
<field name="has_garden">False</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@ -484,9 +487,6 @@ In addition to XML data files, the server framework allows loading data files in
|
||||
format is often more convenient for describing records with simple field values belonging to the
|
||||
same model. It also loads faster, making it the go-to format when performance matters most.
|
||||
|
||||
.. seealso::
|
||||
:ref:`Reference documentation for CSV data files <reference/data/csvdatafiles>`
|
||||
|
||||
.. example::
|
||||
See below for an example of how a subset of `country states can be loaded into Odoo
|
||||
<{GITHUB_PATH}/odoo/addons/base/data/res.country.state.csv>`_.
|
||||
@ -510,7 +510,6 @@ same model. It also loads faster, making it the go-to format when performance ma
|
||||
state_ca_yt,ca,"Yukon","YT"
|
||||
|
||||
.. note::
|
||||
|
||||
- The file name must match the model name.
|
||||
- The first line lists the model fields to populate.
|
||||
- XML IDs are specified via the special `id` field.
|
||||
@ -518,6 +517,9 @@ same model. It also loads faster, making it the go-to format when performance ma
|
||||
as value.
|
||||
- Each subsequent line describes one new record.
|
||||
|
||||
.. seealso::
|
||||
:ref:`Reference documentation for CSV data files <reference/data/csvdatafiles>`
|
||||
|
||||
In business applications like Odoo, one of the first questions to consider is who can access the
|
||||
data. By default, access to newly created models is restricted until it is explicitly granted.
|
||||
Granting access rights is done by creating records of the `ir.model.access` model, which specifies
|
||||
@ -532,7 +534,6 @@ began being logged at server start-up after creating the model:
|
||||
WARNING tutorials odoo.modules.loading: The models ['real.estate.property'] have no access rules [...]
|
||||
|
||||
.. exercise::
|
||||
|
||||
#. Create a new :file:`ir.model.access.csv` file at the root of the `real_estate` module.
|
||||
#. Declare it in the manifest as you did for the :file:`real_estate_property_data.xml` file.
|
||||
#. Grant access to the `real.estate.property` model to all administrators of the database by
|
||||
@ -550,7 +551,7 @@ began being logged at server start-up after creating the model:
|
||||
|
||||
.. spoiler:: Solution
|
||||
|
||||
.. code-block:: py
|
||||
.. code-block:: python
|
||||
:caption: `__manifest__.py`
|
||||
:emphasize-lines: 2
|
||||
|
||||
|
@ -16,8 +16,8 @@ Add menus items
|
||||
different parts of Odoo and can be nested to form a hierarchical structure. This allows the
|
||||
functionalities of complex applications to be organized into categories and sub-categories and makes
|
||||
them easier to navigate. The top level of the menu structure typically contains the menu items for
|
||||
the main applications (like "Contacts", "Sales", and "Accounting"). These top-level menu items can
|
||||
also be visually enhanced with custom icons for better recognition.
|
||||
the main applications (like :guilabel:`Contacts`, :guilabel:`Sales`, and :guilabel:`Accounting`).
|
||||
These top-level menu items can also be visually enhanced with custom icons for better recognition.
|
||||
|
||||
Menu items can take on two distinct roles:
|
||||
|
||||
@ -47,15 +47,16 @@ a data file. Let’s do just that and add menu items to our real estate app!
|
||||
|
||||
.. exercise::
|
||||
#. Create and declare a new :file:`menus.xml` file at the root of the `real_estate` module.
|
||||
#. Describe a new "Real Estate" menu item to serve as root menu for our real estate app.
|
||||
#. Describe a new :guilabel:`Real Estate` menu item to serve as root menu for our real estate
|
||||
app.
|
||||
|
||||
- Leave the `parent_id` field empty to place the menu item in the top-level menu.
|
||||
- Use the `static/description/icon.png` file as `web_icon`, in the format
|
||||
`<module>,<icon_file_path>`.
|
||||
|
||||
#. Nest new "Properties" and "Settings" menu items under the root menu item. As we have not yet
|
||||
created an action to browse properties or open settings, reference the following existing
|
||||
actions instead:
|
||||
#. Nest new :guilabel:`Properties` and :guilabel:`Settings` menu items under the root menu item.
|
||||
As we have not yet created an action to browse properties or open settings, reference the
|
||||
following existing actions instead:
|
||||
|
||||
- `base.open_module_tree` that opens the list of modules.
|
||||
- `base.action_client_base_menu` that opens the general settings.
|
||||
@ -128,7 +129,7 @@ it simplifies the syntax and automatically handles some technical details for yo
|
||||
</menuitem>
|
||||
|
||||
.. note::
|
||||
- The outer `menuitem` data operation creates the top-level "Product" menu item.
|
||||
- The outer `menuitem` data operation creates the top-level :guilabel:`Product` menu item.
|
||||
- The specifications (`name`, `web_icon`, `sequence`, `action`, ...) of menu items are set
|
||||
through attributes of the XML element.
|
||||
- The menu items hierarchy is defined by nesting their XML elements.
|
||||
@ -197,9 +198,6 @@ the `ir.actions.act_window` model whose key fields include:
|
||||
`help`
|
||||
An optional help text for the users when there are no records to display.
|
||||
|
||||
.. seealso::
|
||||
:doc:`Reference documentation for actions <../../reference/backend/actions>`
|
||||
|
||||
.. example::
|
||||
The example below defines an action to open existing products in either list or form view.
|
||||
|
||||
@ -220,16 +218,18 @@ the `ir.actions.act_window` model whose key fields include:
|
||||
The content of the `help` field can be written in different formats thanks to the `type`
|
||||
attribute of the :ref:`field <reference/data/field>` data operation.
|
||||
|
||||
.. seealso::
|
||||
:ref:`Reference documentation for window actions <reference/actions/window>`
|
||||
|
||||
As promised, we'll finally get to interact with our real estate properties in the UI. All we need
|
||||
now is an action to assign to the menu item.
|
||||
|
||||
.. exercise::
|
||||
|
||||
#. Create and declare a new :file:`actions.xml` file at the root of the `real_estate` module.
|
||||
#. Describe a new "Properties" window action that opens `real.estate.property` records in list
|
||||
and form views, and assign it to the "Properties" menu item. Be creative with the help text!
|
||||
For reference, the list of supported classes can be found in the `view.scss
|
||||
<{GITHUB_PATH}/addons/web/static/src/views/view.scss>`_ file.
|
||||
#. Describe a new :guilabel:`Properties` window action that opens `real.estate.property` records
|
||||
in list and form views, and assign it to the :guilabel:`Properties` menu item. Be creative
|
||||
with the help text! For reference, the list of supported classes can be found in the
|
||||
`view.scss <{GITHUB_PATH}/addons/web/static/src/views/view.scss>`_ file.
|
||||
|
||||
.. tip::
|
||||
Pay attention to the declaration order of data files in the manifest; you might introduce a
|
||||
@ -279,9 +279,10 @@ now is an action to assign to the menu item.
|
||||
action="real_estate.view_properties_action"
|
||||
/>
|
||||
|
||||
Clicking the "Properties" menu item now displays a list view of the default properties we created
|
||||
earlier. As we specified in the action that both list and form views were allowed, you can click any
|
||||
property record to display its form view. Delete all three records to see the help text you created.
|
||||
Clicking the :guilabel:`Properties` menu item now displays a list view of the default properties we
|
||||
created earlier. As we specified in the action that both list and form views were allowed, you can
|
||||
click any property record to display its form view. Delete all three records to see the help text
|
||||
you created.
|
||||
|
||||
.. _tutorials/server_framework_101/create_custom_views:
|
||||
|
||||
@ -315,11 +316,6 @@ structure and content of the view. These components can be structural (like `she
|
||||
layout responsive, or `group` that defines column layouts) or semantic (like `field` that displays
|
||||
field labels and values).
|
||||
|
||||
.. seealso::
|
||||
- :doc:`Reference documentation for view records <../../reference/user_interface/view_records>`
|
||||
- :doc:`Reference documentation for view architectures
|
||||
<../../reference/user_interface/view_architectures>`
|
||||
|
||||
.. example::
|
||||
The following examples demonstrate how to define simple list, form and search views for the
|
||||
`product` model.
|
||||
@ -374,10 +370,14 @@ field labels and values).
|
||||
</record>
|
||||
|
||||
.. note::
|
||||
|
||||
- The XML structure differs between view types.
|
||||
- The `description` field is omitted from the list view because it wouldn't fit visually.
|
||||
|
||||
.. seealso::
|
||||
- :doc:`Reference documentation for view records <../../reference/user_interface/view_records>`
|
||||
- :doc:`Reference documentation for view architectures
|
||||
<../../reference/user_interface/view_architectures>`
|
||||
|
||||
In :ref:`the previous section <tutorials/server_framework_101/define_window_actions>`, we defined
|
||||
the `view_mode` of our action to display `real.estate.property` records in list and form view.
|
||||
Although we haven't created the corresponding views yet, the server framework had our back and
|
||||
@ -397,15 +397,17 @@ List view
|
||||
For a start, the list view could use more fields than just the name.
|
||||
|
||||
.. exercise::
|
||||
|
||||
#. Create a new :file:`real_estate_property_views.xml` file at the root of the `real_estate`
|
||||
module.
|
||||
#. Create a custom list view to display the following fields of the `real.estate.property` model
|
||||
in the given order: name, state, type, selling price, availability date, floor area, number of
|
||||
bedrooms, presence of a garden, and presence of a garage.
|
||||
#. Make the visibility of the floor area and all following fields optional so that only the floor
|
||||
area is visible by default, while the remaining fields are hidden by default and must be
|
||||
displayed by accessing the view's column selector (:icon:`oi-settings-adjust` button).
|
||||
in the given order: :guilabel:`Name`, :guilabel:`State`, :guilabel:`Type`,
|
||||
:guilabel:`Selling Price`, :guilabel:`Availability Date`, :guilabel:`Floor Area`,
|
||||
:guilabel:`Number of Bedrooms`, :guilabel:`Garage`, :guilabel:`Garden`, and
|
||||
:guilabel:`Garden Area`.
|
||||
#. Make the visibility of :guilabel:`Floor Area` and all following fields optional so that only
|
||||
the floor area is visible by default, while the remaining fields are hidden by default and
|
||||
must be manually displayed by accessing the view's column selector
|
||||
(:icon:`oi-settings-adjust` button).
|
||||
#. After restarting the server to load the new data, refresh the browser to see the result.
|
||||
|
||||
.. tip::
|
||||
@ -449,8 +451,9 @@ For a start, the list view could use more fields than just the name.
|
||||
<field name="availability_date"/>
|
||||
<field name="floor_area" optional="show"/>
|
||||
<field name="bedrooms" optional="hide"/>
|
||||
<field name="has_garden" optional="hide"/>
|
||||
<field name="has_garage" optional="hide"/>
|
||||
<field name="has_garden" optional="hide"/>
|
||||
<field name="garden_area" optional="hide"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
@ -463,7 +466,6 @@ Form view
|
||||
---------
|
||||
|
||||
.. exercise::
|
||||
|
||||
In the :file:`real_estate_property_views.xml` file, create a custom form view to display all
|
||||
fields of the `real.estate.property` model in a well-structured manner:
|
||||
|
||||
@ -475,8 +477,10 @@ Form view
|
||||
- The image should be displayed as a thumbnail on the right side of the form.
|
||||
- The fields should be grouped in two sections displayed next to each other:
|
||||
|
||||
- Listing Information: Type, Selling Price, Availability Date, Active
|
||||
- Building Specifications: Floor Area, Number of Bedrooms, Garden, Garage
|
||||
- Listing Information: :guilabel:`Type`, :guilabel:`Selling Price`,
|
||||
:guilabel:`Availability Date`, :guilabel:`Active`
|
||||
- Building Specifications: :guilabel:`Floor Area`, :guilabel:`Number of Bedrooms`,
|
||||
:guilabel:`Garage`, :guilabel:`Garden`, :guilabel:`Garden Area`
|
||||
|
||||
- The description should be displayed at the bottom of the form in its own section, should have
|
||||
no label, should have a placeholder, and should take the full width.
|
||||
@ -525,8 +529,9 @@ Form view
|
||||
<group string="Building Specifications">
|
||||
<field name="floor_area"/>
|
||||
<field name="bedrooms"/>
|
||||
<field name="has_garden"/>
|
||||
<field name="has_garage"/>
|
||||
<field name="has_garden"/>
|
||||
<field name="garden_area"/>
|
||||
</group>
|
||||
</group>
|
||||
<separator string="Description"/>
|
||||
@ -555,19 +560,12 @@ automatically excluded from searches. You can observe this behavior by deselecti
|
||||
:guilabel:`Active` checkbox for one of your property records: you'll notice the record no longer
|
||||
appears upon returning to the list view.
|
||||
|
||||
.. seealso::
|
||||
:ref:`Reference documentation for the list of reserved field names
|
||||
<reference/orm/fields/reserved>`
|
||||
|
||||
To facilitate the browsing of archived properties, we need to create a search view. Unlike list and
|
||||
form views, search views are not used to display record data on screen. Instead, they define the
|
||||
search behavior and enable users to search on specific fields. They also provide pre-defined
|
||||
**filters** that allow for quickly searching with complex queries and grouping records by particular
|
||||
fields.
|
||||
|
||||
.. seealso::
|
||||
:ref:`Reference documentation for search views <reference/view_architectures/search>`
|
||||
|
||||
The most common way to set up filters is through **search domains**. Domains are used to select
|
||||
specific records of a model by defining a list of criteria. Each criterion is a triplet in the
|
||||
format :code:`(<field_name>, <operator>, <value>)`.
|
||||
@ -594,32 +592,37 @@ before its operands`.
|
||||
['|', ('category', '=', 'electronics'), '!', '&', ('price', '>=', 1000), ('price', '<', 2000)]
|
||||
|
||||
.. seealso::
|
||||
:ref:`Reference documentation for search domains <reference/orm/domains>`
|
||||
- :ref:`Reference documentation for search views <reference/view_architectures/search>`
|
||||
- :ref:`Reference documentation for search domains <reference/orm/domains>`
|
||||
- :ref:`Reference documentation for the list of reserved field names
|
||||
<reference/orm/fields/reserved>`
|
||||
|
||||
All the generic search view only allows for is searching on property names; that's the bare minimum.
|
||||
Let's enhance the search capabilities.
|
||||
|
||||
.. exercise::
|
||||
|
||||
#. Create a custom search view with the following features:
|
||||
|
||||
- Enable searching on the these fields:
|
||||
|
||||
- Name: Match records whose name contain the search value.
|
||||
- Description: Match records whose description *or* name contains the search value.
|
||||
- Selling price: Match records with a price *less than or equal to* the search value.
|
||||
- Floor area: Match records with a floor area *at least* the search value.
|
||||
- Number of bedrooms: Match records with *at least* the given number of bedrooms.
|
||||
- :guilabel:`Name`: Match records whose name contain the search value.
|
||||
- :guilabel:`Description`: Match records whose description *or* name contains the search
|
||||
value.
|
||||
- :guilabel:`Selling Price`: Match records with a price *less than or equal to* the search
|
||||
value.
|
||||
- :guilabel:`Floor Area`: Match records with a floor area *at least* the search value.
|
||||
- :guilabel:`Number of Bedrooms`: Match records with *at least* the given number of
|
||||
bedrooms.
|
||||
|
||||
- Implement these filters:
|
||||
|
||||
- For Sale: The state is "New" or "Offer Received".
|
||||
- Availability Date: Display a list of pre-defined availability date values.
|
||||
- Garden: The property has a garden.
|
||||
- Garage: The property has a garage.
|
||||
- Archived: The property is archived.
|
||||
- :guilabel:`For Sale`: The state is :guilabel:`New` or :guilabel:`Offer Received`.
|
||||
- :guilabel:`Availability Date`: Display a list of pre-defined availability date values.
|
||||
- :guilabel:`Garage`: The property has a garage.
|
||||
- :guilabel:`Garden`: The property has a garden.
|
||||
- :guilabel:`Archived`: The property is archived.
|
||||
|
||||
- Combine selected filters with a logical AND, except for Garden and Garage, which should use
|
||||
- Combine selected filters with a logical AND, except for Garage and Garden, which should use
|
||||
OR when both are selected.
|
||||
- Enable grouping properties by state and type.
|
||||
|
||||
@ -672,8 +675,8 @@ Let's enhance the search capabilities.
|
||||
<separator/>
|
||||
<filter name="filter_availability" date="availability_date"/>
|
||||
<separator/>
|
||||
<filter name="filter_garden" string="Garden" domain="[('has_garden', '=', True)]"/>
|
||||
<filter name="filter_garage" string="Garage" domain="[('has_garage', '=', True)]"/>
|
||||
<filter name="filter_garden" string="Garden" domain="[('has_garden', '=', True)]"/>
|
||||
<separator/>
|
||||
<filter name="filter_inactive" string="Archived" domain="[('active', '=', False)]"/>
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 28 KiB |
Binary file not shown.
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 25 KiB |
@ -10,8 +10,8 @@ of our real estate application.
|
||||
|
||||
.. _tutorials/server_framework_101/module_structure:
|
||||
|
||||
Module structure
|
||||
================
|
||||
Organize your module structure
|
||||
==============================
|
||||
|
||||
As our `real_estate` module grows, you may notice that we've already created a dozen files for just
|
||||
one model, along with its menu items, actions and views. With more models on the horizon, our module
|
||||
@ -25,10 +25,6 @@ structure guidelines** that offer several benefits:
|
||||
- **Collaboration**: A standardized structure facilitates understanding among contributors and
|
||||
ensures easier integration with the Odoo ecosystem.
|
||||
|
||||
.. seealso::
|
||||
:ref:`Coding guidelines on module directories
|
||||
<contributing/coding_guidelines/module_structure/directories>`
|
||||
|
||||
.. example::
|
||||
Let's consider a possible structure for our example `product` module:
|
||||
|
||||
@ -62,7 +58,6 @@ structure guidelines** that offer several benefits:
|
||||
└── __manifest__.py
|
||||
|
||||
.. note::
|
||||
|
||||
- The :file:`models` directory contains its own :file:`__init__.py` file, simplifying Python
|
||||
imports. The root :file:`__init__.py` file imports the :file:`models` Python package, which
|
||||
in turns imports individual model files.
|
||||
@ -77,8 +72,11 @@ structure guidelines** that offer several benefits:
|
||||
- The :file:`__init__.py` and :file:`__manifest__.py` files remain in the module's root
|
||||
directory.
|
||||
|
||||
.. exercise::
|
||||
.. seealso::
|
||||
:ref:`Coding guidelines on module directories
|
||||
<contributing/coding_guidelines/module_structure/directories>`
|
||||
|
||||
.. exercise::
|
||||
Restructure the `real_estate` module according to the guidelines.
|
||||
|
||||
.. tip::
|
||||
@ -193,8 +191,8 @@ structure guidelines** that offer several benefits:
|
||||
|
||||
.. _tutorials/server_framework_101/many2one:
|
||||
|
||||
Many-to-one
|
||||
===========
|
||||
Many-to-one relationships
|
||||
=========================
|
||||
|
||||
As promised at the end of :doc:`the previous chapter <03_build_user_interface>`, we'll now expand
|
||||
our app's capabilities by adding new models to manage additional information. This expansion
|
||||
@ -211,14 +209,11 @@ representing the *many* side of the relationship. The field is represented in th
|
||||
record. By convention, `Many2one` field names end with the `_id` suffix, indicating that they store
|
||||
the referenced record's ID.
|
||||
|
||||
.. seealso::
|
||||
:ref:`Reference documentation for Many2one fields <reference/fields/many2one>`
|
||||
|
||||
.. example::
|
||||
In the example below, the `Selection` field of the `product` model is replaced by a `Many2one`
|
||||
field to create a more flexible and scalable model structure.
|
||||
|
||||
.. code-block:: py
|
||||
.. code-block:: python
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
@ -239,17 +234,18 @@ the referenced record's ID.
|
||||
name = fields.Char(string="Name")
|
||||
|
||||
.. note::
|
||||
|
||||
- The relationship only needs to be declared on the *many* side to be established.
|
||||
- The `ondelete` argument on the `Many2one` field defines what happens when the referenced
|
||||
record is deleted.
|
||||
|
||||
.. seealso::
|
||||
:ref:`Reference documentation for Many2one fields <reference/fields/many2one>`
|
||||
|
||||
In our real estate app, we currently have a fixed set of property types. To increase flexibility,
|
||||
let's replace the current `type` field with a many-to-one relationship to a separate model for
|
||||
managing property types.
|
||||
|
||||
.. exercise::
|
||||
|
||||
#. Create a new `real.estate.property.type` model.
|
||||
|
||||
- Update the :file:`ir.model.access.csv` file to grant all database administrators access to
|
||||
@ -258,12 +254,12 @@ managing property types.
|
||||
--> Property Types` menu item.
|
||||
- Create a window action to browse property types only in list view.
|
||||
- Create the list view for property types.
|
||||
- In a data file, describe at least as many default property types as the `type` field of the
|
||||
`real.estate.property` model supports.
|
||||
- In a data file, describe at least as many default property types as the :guilabel:`Type`
|
||||
field of the `real.estate.property` model supports.
|
||||
|
||||
#. Replace the `type` field on the `real.estate.property` model by a many-to-one relationship to
|
||||
the `real.estate.property.type` model. Prevent deleting property types if a property
|
||||
references them.
|
||||
#. Replace the :guilabel:`Type` field on the `real.estate.property` model by a many-to-one
|
||||
relationship to the `real.estate.property.type` model. Prevent deleting property types if a
|
||||
property references them.
|
||||
|
||||
.. tip::
|
||||
|
||||
@ -281,7 +277,6 @@ managing property types.
|
||||
:caption: `real_estate_property_type.py`
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo.tools import date_utils
|
||||
|
||||
|
||||
class RealEstatePropertyType(models.Model):
|
||||
@ -290,7 +285,7 @@ managing property types.
|
||||
|
||||
name = fields.Char(string="Name", required=True)
|
||||
|
||||
.. code-block:: py
|
||||
.. code-block:: python
|
||||
:caption: `__init__.py`
|
||||
:emphasize-lines: 2
|
||||
|
||||
@ -372,7 +367,7 @@ managing property types.
|
||||
|
||||
</odoo>
|
||||
|
||||
.. code-block:: py
|
||||
.. code-block:: python
|
||||
:caption: `__manifest__.py`
|
||||
:emphasize-lines: 3,4,7,9
|
||||
|
||||
@ -387,7 +382,7 @@ managing property types.
|
||||
'views/menus.xml', # Depends on actions in views.
|
||||
],
|
||||
|
||||
.. code-block:: py
|
||||
.. code-block:: python
|
||||
:caption: `real_estate_property.py`
|
||||
:emphasize-lines: 1-3
|
||||
|
||||
@ -473,35 +468,40 @@ Two frequently used models in Odoo are:
|
||||
.. seealso::
|
||||
`The list of generic models in the base module <{GITHUB_PATH}/odoo/addons/base/models>`_
|
||||
|
||||
To make our real estate properties more informative, let's add two pieces of information: the seller
|
||||
of the property and the salesperson managing the property.
|
||||
To make our real estate properties more informative, let's add three pieces of information: the
|
||||
seller of the property, the salesperson managing the property, and the address of the property.
|
||||
|
||||
.. exercise::
|
||||
|
||||
#. Add the following fields to the `real.estate.property` model:
|
||||
|
||||
- Seller (required): The person putting their property on sale; it can be any individual.
|
||||
- Salesperson: The employee of the real estate agency overseeing the sale of the property.
|
||||
- :guilabel:`Seller` (required): The person putting their property on sale; it can be any
|
||||
individual.
|
||||
- :guilabel:`Salesperson`: The employee of the real estate agency overseeing the sale of the
|
||||
property.
|
||||
- :guilabel:`Address` (required): The address of the property.
|
||||
|
||||
#. Modify the form view of properties to include a notebook component. The property description
|
||||
should be in the first page, and the two new fields should be in the second page.
|
||||
should be in the first page, and the three new fields should be in the second page.
|
||||
|
||||
.. tip::
|
||||
You don't need to define any new UI component to browse the seller you assigned to your
|
||||
default properties! Just go to :menuselection:`Apps` and install the :guilabel:`Contacts` app.
|
||||
- You don't need to define any new UI component to browse the seller you assigned to your
|
||||
default properties! Just go to :menuselection:`Apps` and install the :guilabel:`Contacts`
|
||||
app.
|
||||
- In Odoo, addresses are usually represented by a partner.
|
||||
|
||||
.. spoiler:: Solution
|
||||
|
||||
.. code-block:: python
|
||||
:caption: `real_estate_property.py`
|
||||
:emphasize-lines: 1-2
|
||||
:emphasize-lines: 1-3
|
||||
|
||||
address_id = fields.Many2one(string="Address", comodel_name='res.partner', required=True)
|
||||
seller_id = fields.Many2one(string="Seller", comodel_name='res.partner', required=True)
|
||||
salesperson_id = fields.Many2one(string="Salesperson", comodel_name='res.users')
|
||||
|
||||
.. code-block:: xml
|
||||
:caption: `real_estate_property_views.xml`
|
||||
:emphasize-lines: 3-18
|
||||
:emphasize-lines: 3-19
|
||||
|
||||
<record id="real_estate.property_form" model="ir.ui.view">
|
||||
[...]
|
||||
@ -515,6 +515,7 @@ of the property and the salesperson managing the property.
|
||||
<page string="Other Info">
|
||||
<group>
|
||||
<group>
|
||||
<field name="address_id"/>
|
||||
<field name="seller_id"/>
|
||||
<field name="salesperson_id"/>
|
||||
</group>
|
||||
@ -530,6 +531,30 @@ of the property and the salesperson managing the property.
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="real_estate.country_house_address" model="res.partner">
|
||||
<field name="name">Country House</field>
|
||||
<field name="street">Chaussée de Namur 40</field>
|
||||
<field name="city">Grand-Rosière-Hottomont</field>
|
||||
<field name="zip">1367</field>
|
||||
<field name="country_id" ref="base.be"/>
|
||||
</record>
|
||||
|
||||
<record id="real_estate.loft_address" model="res.partner">
|
||||
<field name="name">Loft</field>
|
||||
<field name="street">Rue des Bourlottes 9</field>
|
||||
<field name="city">Grand-Rosière-Hottomont</field>
|
||||
<field name="zip">1367</field>
|
||||
<field name="country_id" ref="base.be"/>
|
||||
</record>
|
||||
|
||||
<record id="real_estate.mixed_use_commercial_address" model="res.partner">
|
||||
<field name="name">Mixed use commercial building</field>
|
||||
<field name="street">Rue de Ramillies 1</field>
|
||||
<field name="city">Grand-Rosière-Hottomont</field>
|
||||
<field name="zip">1367</field>
|
||||
<field name="country_id" ref="base.be"/>
|
||||
</record>
|
||||
|
||||
<record id="real_estate.bafien_carpink" model="res.partner">
|
||||
<field name="name">Bafien Carpink</field>
|
||||
</record>
|
||||
@ -546,24 +571,27 @@ of the property and the salesperson managing the property.
|
||||
|
||||
.. code-block:: xml
|
||||
:caption: `real_estate_property_data.xml`
|
||||
:emphasize-lines: 3,8,13
|
||||
:emphasize-lines: 3-4,9-10,15-16
|
||||
|
||||
<record id="real_estate.country_house" model="real.estate.property">
|
||||
[...]
|
||||
<field name="address_id" ref="real_estate.country_house_address"/>
|
||||
<field name="seller_id" ref="real_estate.amyfromthevideos"/>
|
||||
</record>
|
||||
|
||||
<record id="real_estate.loft" model="real.estate.property">
|
||||
[...]
|
||||
<field name="address_id" ref="real_estate.loft_address"/>
|
||||
<field name="seller_id" ref="real_estate.antony_petisuix"/>
|
||||
</record>
|
||||
|
||||
<record id="real_estate.mixed_use_commercial" model="real.estate.property">
|
||||
[...]
|
||||
<field name="address_id" ref="real_estate.mixed_use_commercial_address"/>
|
||||
<field name="seller_id" ref="real_estate.bafien_carpink"/>
|
||||
</record>
|
||||
|
||||
.. code-block:: py
|
||||
.. code-block:: python
|
||||
:caption: `__manifest__.py`
|
||||
:emphasize-lines: 3,5,6
|
||||
|
||||
@ -578,8 +606,8 @@ of the property and the salesperson managing the property.
|
||||
|
||||
.. _tutorials/server_framework_101/one2many:
|
||||
|
||||
One-to-many
|
||||
===========
|
||||
One-to-many relationships
|
||||
=========================
|
||||
|
||||
After exploring how to connect multiple records to a single one with many-to-one relationships,
|
||||
let's consider their counterparts: **one-to-many relationships**. These relationships represent the
|
||||
@ -592,14 +620,11 @@ note that `One2many` fields don't store data in the database; instead, they prov
|
||||
that Odoo computes based on the referenced `Many2one` field. By convention, `One2many` field names
|
||||
end with the `_ids` suffix, indicating that they allow accessing the IDs of the connected records.
|
||||
|
||||
.. seealso::
|
||||
:ref:`Reference documentation for One2many fields <reference/fields/one2many>`
|
||||
|
||||
.. example::
|
||||
In the example below, a `One2many` field is added to the `product.category` model to allow quick
|
||||
access to the connected products from the product category.
|
||||
|
||||
.. code-block:: py
|
||||
.. code-block:: python
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
@ -623,28 +648,33 @@ end with the `_ids` suffix, indicating that they allow accessing the IDs of the
|
||||
)
|
||||
|
||||
.. note::
|
||||
|
||||
The `One2many` field must reference its `Many2one` counterpart through the `inverse_name`
|
||||
argument.
|
||||
|
||||
.. seealso::
|
||||
:ref:`Reference documentation for One2many fields <reference/fields/one2many>`
|
||||
|
||||
A good use case for a one-to-many relationship in our real estate app would be to connect properties
|
||||
to a list of offers received from potential buyers.
|
||||
|
||||
.. exercise::
|
||||
|
||||
#. Create a new `real.estate.offer` model. It should have the following fields:
|
||||
|
||||
- Amount (required): The amount offered to buy the property.
|
||||
- Buyer (required): The person making the offer.
|
||||
- Date (required; default to creation date): When the offer was made.
|
||||
- Validity (default to 7): The number of days before the offer expires.
|
||||
- State (required): Either "Waiting", "Accepted", or "Refused".
|
||||
- :guilabel:`Amount` (required): The amount offered to buy the property.
|
||||
- :guilabel:`Buyer` (required): The person making the offer.
|
||||
- :guilabel:`Date` (required): When the offer was made.
|
||||
- :guilabel:`Validity` (defaults to 7): The number of days before the offer expires.
|
||||
- :guilabel:`State` (required): Either :guilabel:`Waiting`, :guilabel:`Accepted`, or
|
||||
:guilabel:`Refused`.
|
||||
|
||||
#. Create a list and form views for the `real.estate.offer` model. It's not necessary to create
|
||||
menu items or actions, as offers will be accessible from properties, but feel free to do it
|
||||
anyway!
|
||||
#. Allow connecting properties to multiple offers.
|
||||
#. Modify the form view of properties to display offers in a new notebook page titled "Offers".
|
||||
#. Modify the form view of properties to display offers in a new notebook page titled
|
||||
:guilabel:`Offers`.
|
||||
|
||||
|
||||
|
||||
.. spoiler:: Solution
|
||||
|
||||
@ -660,7 +690,7 @@ to a list of offers received from potential buyers.
|
||||
|
||||
amount = fields.Float(string="Amount", required=True)
|
||||
buyer_id = fields.Many2one(string="Buyer", comodel_name='res.partner', required=True)
|
||||
date = fields.Date(string="Date", required=True, default=fields.Date.today())
|
||||
date = fields.Date(string="Date", required=True)
|
||||
validity = fields.Integer(
|
||||
string="Validity", help="The number of days before the offer expires.", default=7
|
||||
)
|
||||
@ -752,7 +782,7 @@ to a list of offers received from potential buyers.
|
||||
'views/menus.xml', # Depends on actions in views.
|
||||
],
|
||||
|
||||
.. code-block:: py
|
||||
.. code-block:: python
|
||||
:caption: `real_estate_property.py`
|
||||
:emphasize-lines: 1-3
|
||||
|
||||
@ -774,8 +804,8 @@ to a list of offers received from potential buyers.
|
||||
|
||||
.. _tutorials/server_framework_101/many2many:
|
||||
|
||||
Many-to-many
|
||||
============
|
||||
Many-to-many relationships
|
||||
==========================
|
||||
|
||||
After the many-to-one and one-to-many relationships, let's consider a more complex use case:
|
||||
**many-to-many relationships**. These relationships enable *multiple* records in one model to be
|
||||
@ -788,14 +818,11 @@ intermediate (junction) table in the database. This table stores pairs of IDs, e
|
||||
representing a connection between a record of the first model and a record of the second model. By
|
||||
convention, `Many2many` field names end with the `_ids` suffix, like for `One2many` fields.
|
||||
|
||||
.. seealso::
|
||||
:ref:`Reference documentation for Many2many fields <reference/fields/many2many>`
|
||||
|
||||
.. example::
|
||||
In the example below, a many-to-many relationship is established between the `product` model and
|
||||
the `res.partner` model, which is used to represent sellers offering products for sale.
|
||||
|
||||
.. code-block:: py
|
||||
.. code-block:: python
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
@ -815,22 +842,23 @@ convention, `Many2many` field names end with the `_ids` suffix, like for `One2ma
|
||||
)
|
||||
|
||||
.. note::
|
||||
|
||||
- It is not necessary to add a `Many2many` field to both models of the relationship.
|
||||
- The optional `relation`, `column1`, and `column2` field arguments allow specifying the name
|
||||
of the junction table and of its columns.
|
||||
|
||||
.. seealso::
|
||||
:ref:`Reference documentation for Many2many fields <reference/fields/many2many>`
|
||||
|
||||
Let's conclude this extension of the model family by allowing to associate multiple description tags
|
||||
with each property.
|
||||
|
||||
.. exercise::
|
||||
|
||||
#. Create a new `real.estate.tag` model. It should have the following fields:
|
||||
|
||||
- Name (required): The label of the tag.
|
||||
- Color: The color code to use for the tag, as an integer.
|
||||
- :guilabel:`Name` (required): The label of the tag.
|
||||
- :guilabel:`Color`: The color code to use for the tag, as an integer.
|
||||
|
||||
#. In a data file, describe various default property tags. For example, "Renovated".
|
||||
#. In a data file, describe various default property tags. For example, :guilabel:`Renovated`.
|
||||
#. Create all necessary UI components to manage tags from the :guilabel:`Configuration` category
|
||||
menu item.
|
||||
#. Allow connecting properties to multiple tags, and tags to multiple properties.
|
||||
@ -982,7 +1010,7 @@ with each property.
|
||||
<field
|
||||
name="tag_ids"
|
||||
widget="many2many_tags"
|
||||
options="{'color_field': 'color', 'no_quick_create': True, 'no_create_edit': True}"
|
||||
options="{'color_field': 'color', 'no_quick_create': True, 'no_create': True}"
|
||||
/>
|
||||
[...]
|
||||
</record>
|
||||
|
@ -1,35 +0,0 @@
|
||||
=========================
|
||||
Chapter 5: Business logic
|
||||
=========================
|
||||
|
||||
tmp
|
||||
|
||||
.. todo: constraints, defaults, onchanges, computes
|
||||
.. todo: model actions ("assign myself as salesperson" action, "view offers" statbutton)
|
||||
.. todo: explain the env (self.env.cr, self.env.uid, self.env.user, self.env.context, self.env.ref(xml_id), self.env[model_name])
|
||||
.. todo: explain the thing about `self`
|
||||
.. todo: explain magic commands
|
||||
.. todo: copy=False on some fields
|
||||
.. todo: introduce lambda functions for defaults :point_down:
|
||||
|
||||
There is a problem with the way we defined our Date fields: their default value relies on
|
||||
:code:`fields.Date.today()` or some other static method. When the code is loaded into memory, the
|
||||
date is computed once and reused for all newly created records until the server is shut down. You
|
||||
probably didn't notice it, unless you kept your server running for several days, but it would be
|
||||
much more visible with Datetime fields, as all newly created records would share the same timestamp.
|
||||
|
||||
That's where lambda functions come in handy. As they generate an anonymous function each time
|
||||
they're evaluated at runtime, they can be used in the computation of default field values to return
|
||||
an updated value for each new record.
|
||||
|
||||
.. todo: salesperson_id = fields.Many2one(default=lambda self: self.env.user)
|
||||
.. todo: real.estate.offer.amount::default -> property.selling_price
|
||||
.. todo: real.estate.tag.color -> default=_default_color ; def _default_color(self): return random.randint(1, 11)
|
||||
.. todo: 6,0,0 to associate tags to properties in data
|
||||
.. todo: unique tag
|
||||
|
||||
.. todo: odoo-bin shell section
|
||||
|
||||
----
|
||||
|
||||
.. todo: add incentive for chapter 6
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,12 @@
|
||||
===================
|
||||
Chapter 6: Security
|
||||
===================
|
||||
===========================
|
||||
Chapter 6: Tighten security
|
||||
===========================
|
||||
|
||||
tmp
|
||||
|
||||
.. todo: restrict access through acl
|
||||
.. todo: rule to only see your properties or unassigned ones
|
||||
|
||||
----
|
||||
|
||||
.. todo: add incentive for next chapter
|
||||
|
@ -4,11 +4,17 @@ Chapter 7: Advanced views
|
||||
|
||||
tmp
|
||||
|
||||
.. todo:: invisible, required, readonly modifiers
|
||||
.. todo:: introduce bootstrap
|
||||
.. todo:: widgets; eg, <widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
|
||||
.. todo:: add Gantt view of properties availability
|
||||
.. todo:: add Kanban view of properties
|
||||
.. todo: invisible, required, readonly modifiers
|
||||
.. todo: introduce bootstrap
|
||||
.. todo: widgets; eg, <widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
|
||||
<field name="is_priority" widget="boolean_favorite" nolabel="1" readonly="False"/> (/!\ requires to have product installed to have the correct font-size in form view)
|
||||
.. todo: add Gantt view of properties availability
|
||||
.. todo: add Kanban view of properties
|
||||
.. todo: wizards -> create a "receive offer wizard" to default the amount to the property's selling price
|
||||
.. todo: context active_test False on the category_id field of products to see archived categories
|
||||
.. todo: sequence widget on tags
|
||||
.. todo: compute display_name for offers in form view
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
@ -4,7 +4,13 @@ Chapter 8: Inheritance
|
||||
|
||||
tmp
|
||||
|
||||
.. todo:: inherit from mail.tread mixin and add a chatter
|
||||
.. todo: inherit from mail.tread mixin and add a chatter
|
||||
.. todo: self.env._context
|
||||
.. todo: inherit from account.move
|
||||
.. todo: explain magic commands
|
||||
.. todo: ex: override of real.estate.property::create to set vals['address_id'] = (0, 0, {})
|
||||
.. todo: ex: create invoice with lines (6, 0, 0)
|
||||
.. todo: ex: 6,0,0 to associate tags to properties in data
|
||||
|
||||
----
|
||||
|
||||
|
@ -78,7 +78,7 @@ GitHub.
|
||||
#. Once you have pushed your first change to the shared fork on **odoo-dev**, create a **draft**
|
||||
:abbr:`PR (Pull Request)` with your quadrigram in the title. This will enable you to share
|
||||
your upcoming work and receive feedback from your coaches. To ensure a continuous feedback
|
||||
loop, push a new commit as soon as you complete a chapter of a tutorial.
|
||||
loop, push a new commit as soon as you complete an exercise of a tutorial.
|
||||
#. At Odoo we use `Runbot <https://runbot.odoo.com>`_ extensively for our :abbr:`CI (Continuous
|
||||
Integration)` tests. When you push your changes to **odoo-dev**, Runbot creates a new build
|
||||
and tests your code. Once logged in, you will be able to see your branch on the `Tutorials
|
||||
|
Loading…
Reference in New Issue
Block a user