@@ -65,19 +65,15 @@ classes, fields are defined as class attributes. Each field is an instance of a
65
65
`odoo.fields ` package. For example, `Char `, `Float `, `Boolean `, each designed to handle different
66
66
types of data. When defining a field, developers can pass various arguments to finely control how
67
67
data is handled and presented in Odoo. For example, `string ` defines the label for the field in the
68
- user interface, `help ` provides a tooltip when hovering the field in the user interface, and
69
- ` required ` makes filling in the field mandatory.
68
+ user interface, `help ` provides a tooltip when hovering the field in the user interface, ` required `
69
+ makes filling in the field mandatory, and ` default ` provides a default field value .
70
70
71
71
Individual data entries are called **records **. They are based on the structure defined by models
72
72
and contain the actual data for each field specified in the model. In Python, records are
73
73
represented as instances of the model's class, allowing developers to interact with data using
74
74
object-oriented programming techniques. For example, in a real estate application using a tenant
75
75
model, each specific tenant (such as "Bafien Carpink") would be a separate record of that model.
76
76
77
- .. seealso ::
78
- For the full list of fields and their attributes, see the :ref: `reference documentation
79
- <reference/orm/fields>`.
80
-
81
77
.. example ::
82
78
Before we dive into creating our own models, let's take a look at a basic example of a model that
83
79
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
94
90
95
91
name = fields.Char(string = " Name" , required = True )
96
92
description = fields.Text(string = " Description" )
97
- price = fields.Float(string = " Sale Price" , required = True )
93
+ price = fields.Float(string = " Sales Price" , required = True )
98
94
category = fields.Selection(
99
95
string = " Category" ,
100
96
help = " The category of the product; if none are suitable, select 'Other'." ,
101
97
selection = [
102
- (' apparel' , " Clothing" )
98
+ (' apparel' , " Clothing" ),
103
99
(' electronics' , " Electronics" ),
104
100
(' home_decor' , " Home Decor" ),
105
101
(' other' , " Other" ),
@@ -116,11 +112,14 @@ model, each specific tenant (such as "Bafien Carpink") would be a separate recor
116
112
- `category ` is a `Selection ` field with predefined options, each defined by a technical code
117
113
and a corresponding label. Since it is required, a default value is provided.
118
114
115
+ .. seealso ::
116
+ For the full list of fields and their attributes, see the :ref: `reference documentation
117
+ <reference/orm/fields>`.
118
+
119
119
Building on these new concepts, let's now create the first model for our real estate app. We'll
120
120
create a model with some fields to represent real estate properties and their characteristics.
121
121
122
122
.. exercise ::
123
-
124
123
#. Create a new :file: `real_estate_property.py ` file at the root of the `real_estate ` module.
125
124
#. Update the :file: `real_estate/__init__.py ` file to relatively import the
126
125
:file: `real_estate_property.py ` file, like so:
@@ -132,19 +131,20 @@ create a model with some fields to represent real estate properties and their ch
132
131
#. Define a new model with `real.estate.property ` as `_name ` and a short `_description `.
133
132
#. Add fields to represent the following characteristics:
134
133
135
- - Name (required)
136
- - Description
137
- - Image (max 600x400 pixels)
138
- - Active (default to true)
139
- - State (new, offer received, under option, or sold; required; default to new)
140
- - Type (house, apartment, office building, retail space, or warehouse; required; default to
134
+ - ** Name ** (required)
135
+ - ** Description **
136
+ - ** Image ** (max 600x400 pixels)
137
+ - ** Active ** (whether the property listing is active; defaults to true)
138
+ - ** State ** (new, offer received, under option, or sold; required; defaults to new)
139
+ - ** Type ** (house, apartment, office building, retail space, or warehouse; required; defaults to
141
140
house)
142
- - Selling Price (without currency; with help text; required)
143
- - Availability Date (default to creation date + two months)
144
- - Floor Area (in square meters; with help text)
145
- - Number of Bedrooms (default to two)
146
- - Whether there is a garden
147
- - Whether there is a garage
141
+ - **Selling Price ** (without currency; with help text; required)
142
+ - **Availability Date **
143
+ - **Floor Area ** (in square meters; with help text)
144
+ - **Number of Bedrooms ** (defaults to two)
145
+ - **Garage ** (whether there is a garage)
146
+ - **Garden ** (whether there is a garden)
147
+ - **Garden Area ** (in square meters; with help text)
148
148
149
149
.. tip ::
150
150
- The class name doesn't matter, but the convention is to use the model's upper-cased `_name `
@@ -163,7 +163,6 @@ create a model with some fields to represent real estate properties and their ch
163
163
:caption: `real_estate_property.py`
164
164
165
165
from odoo import fields, models
166
- from odoo.tools import date_utils
167
166
168
167
169
168
class RealEstateProperty (models .Model ):
@@ -200,15 +199,16 @@ create a model with some fields to represent real estate properties and their ch
200
199
selling_price = fields.Float(
201
200
string = " Selling Price" , help = " The selling price excluding taxes." , required = True
202
201
)
203
- availability_date = fields.Date(
204
- string = " Availability Date" , default = date_utils.add(fields.Date.today(), months = 2 )
205
- )
202
+ availability_date = fields.Date(string = " Availability Date" )
206
203
floor_area = fields.Integer(
207
204
string = " Floor Area" , help = " The floor area in square meters excluding the garden."
208
205
)
209
206
bedrooms = fields.Integer(string = " Number of bedrooms" , default = 2 )
210
- has_garden = fields.Boolean(string = " Garden" )
211
207
has_garage = fields.Boolean(string = " Garage" )
208
+ has_garden = fields.Boolean(string = " Garden" )
209
+ garden_area = fields.Integer(
210
+ string = " Garden Area" , help = " The garden area excluding the building."
211
+ )
212
212
213
213
Congrats, you have just defined the first model of our real estate app! However, the changes have
214
214
not yet been applied to the database. To do so, you must add the `-u real_estate ` argument to the
@@ -235,7 +235,6 @@ you created translate into a new SQL table. We will use `psql`, the CLI
235
235
:dfn: `command-line interface ` allowing to browse and interact with PostgreSQL databases.
236
236
237
237
.. exercise ::
238
-
239
238
#. In your terminal, execute the command :command: `psql -d tutorials `.
240
239
#. Enter the command :command: `\\ d real_estate_property ` to print the description of the
241
240
`real_estate_property ` table.
@@ -246,6 +245,7 @@ you created translate into a new SQL table. We will use `psql`, the CLI
246
245
.. spoiler :: Solution
247
246
248
247
.. code-block :: text
248
+ :caption: terminal
249
249
250
250
$ psql -d tutorials
251
251
@@ -256,6 +256,7 @@ you created translate into a new SQL table. We will use `psql`, the CLI
256
256
id | integer | | not null | nextval('real_estate_property_id_seq'::regclass)
257
257
floor_area | integer | | |
258
258
bedrooms | integer | | |
259
+ garden_area | integer | | |
259
260
create_uid | integer | | |
260
261
write_uid | integer | | |
261
262
name | character varying | | not null |
@@ -264,8 +265,8 @@ you created translate into a new SQL table. We will use `psql`, the CLI
264
265
availability_date | date | | |
265
266
description | text | | |
266
267
active | boolean | | |
267
- has_garden | boolean | | |
268
268
has_garage | boolean | | |
269
+ has_garden | boolean | | |
269
270
create_date | timestamp without time zone | | |
270
271
write_date | timestamp without time zone | | |
271
272
selling_price | double precision | | not null |
@@ -343,9 +344,6 @@ The most common data operation is creating new records through the `record` and
343
344
elements, but other operations exist, such as `delete `, which deletes previously created records, or
344
345
even `function `, which allows executing arbitrary code.
345
346
346
- .. seealso ::
347
- :doc: `Reference documentation for XML data files <../../reference/backend/data >`
348
-
349
347
Some data operations require their data elements to be uniquely identified by the system. This is
350
348
achieved by means of the `id ` attribute, also known as the **XML ID ** or **external identifier **. It
351
349
provides a way for other elements to reference it with the `ref ` attribute and links data elements
@@ -387,10 +385,12 @@ created from a data file so that records can be referenced by their full XML ID
387
385
- The `ref ` attribute is used to reference other records by their XML ID and use their record
388
386
ID as value.
389
387
388
+ .. seealso ::
389
+ :doc: `Reference documentation for XML data files <../../reference/backend/data >`
390
+
390
391
Let's now load some default real estate properties in our database.
391
392
392
393
.. exercise ::
393
-
394
394
#. Create a new :file: `real_estate_property_data.xml ` file at the root of the `real_estate `
395
395
module.
396
396
#. Update the manifest to let the server know that it should load our data file. To do so, have
@@ -441,10 +441,12 @@ Let's now load some default real estate properties in our database.
441
441
<field name =" image" type =" base64" file =" real_estate/country_house.png" />
442
442
<field name =" type" >house</field >
443
443
<field name =" selling_price" >745000</field >
444
+ <field name =" availability_date" >2024-08-01</field >
444
445
<field name =" floor_area" >416</field >
445
446
<field name =" bedrooms" >5</field >
446
- <field name =" has_garden" >True</field >
447
447
<field name =" has_garage" >True</field >
448
+ <field name =" has_garden" >True</field >
449
+ <field name =" garden_area" >2100</field >
448
450
</record >
449
451
450
452
<record id =" real_estate.loft" model =" real.estate.property" >
@@ -456,8 +458,8 @@ Let's now load some default real estate properties in our database.
456
458
<field name =" availability_date" >2025-01-01</field >
457
459
<field name =" floor_area" >195</field >
458
460
<field name =" bedrooms" >3</field >
459
- <field name =" has_garden" >False</field >
460
461
<field name =" has_garage" >True</field >
462
+ <field name =" has_garden" >False</field >
461
463
</record >
462
464
463
465
<record id =" real_estate.mixed_use_commercial" model =" real.estate.property" >
@@ -469,8 +471,8 @@ Let's now load some default real estate properties in our database.
469
471
<field name =" availability_date" >2024-10-02</field >
470
472
<field name =" floor_area" >370</field >
471
473
<field name =" bedrooms" >0</field >
472
- <field name =" has_garden" >False</field >
473
474
<field name =" has_garage" >False</field >
475
+ <field name =" has_garden" >False</field >
474
476
</record >
475
477
476
478
</odoo >
@@ -484,9 +486,6 @@ In addition to XML data files, the server framework allows loading data files in
484
486
format is often more convenient for describing records with simple field values belonging to the
485
487
same model. It also loads faster, making it the go-to format when performance matters most.
486
488
487
- .. seealso ::
488
- :ref: `Reference documentation for CSV data files <reference/data/csvdatafiles >`
489
-
490
489
.. example ::
491
490
See below for an example of how a subset of `country states can be loaded into Odoo
492
491
<{GITHUB_PATH}/odoo/addons/base/data/res.country.state.csv> `_.
@@ -510,14 +509,16 @@ same model. It also loads faster, making it the go-to format when performance ma
510
509
state_ca_yt,ca,"Yukon","YT"
511
510
512
511
.. note ::
513
-
514
512
- The file name must match the model name.
515
513
- The first line lists the model fields to populate.
516
514
- XML IDs are specified via the special `id ` field.
517
515
- The `:id ` suffix is used to reference other records by their XML ID and use their record ID
518
516
as value.
519
517
- Each subsequent line describes one new record.
520
518
519
+ .. seealso ::
520
+ :ref: `Reference documentation for CSV data files <reference/data/csvdatafiles >`
521
+
521
522
In business applications like Odoo, one of the first questions to consider is who can access the
522
523
data. By default, access to newly created models is restricted until it is explicitly granted.
523
524
Granting access rights is done by creating records of the `ir.model.access ` model, which specifies
@@ -532,7 +533,6 @@ began being logged at server start-up after creating the model:
532
533
WARNING tutorials odoo.modules.loading: The models ['real.estate.property'] have no access rules [...]
533
534
534
535
.. exercise ::
535
-
536
536
#. Create a new :file: `ir.model.access.csv ` file at the root of the `real_estate ` module.
537
537
#. Declare it in the manifest as you did for the :file: `real_estate_property_data.xml ` file.
538
538
#. Grant access to the `real.estate.property ` model to all administrators of the database by
@@ -550,7 +550,7 @@ began being logged at server start-up after creating the model:
550
550
551
551
.. spoiler :: Solution
552
552
553
- .. code-block :: py
553
+ .. code-block :: python
554
554
:caption: `__manifest__.py`
555
555
:emphasize- lines: 2
556
556
0 commit comments