Odoo Document
Odoo Document
Table of Contents 1
Induction to Odoo 5
Architecture Overview 6
Versions of odoo 7
Odoo modules 8
Composition of a module 8
How to create a workspace? 9
How to run/start odoo? 10
Play with odoo 15
Home Menu 15
Apps 15
Install some apps 15
Menu items 15
Explore sale 15
Stop odoo local server 18
Restart odoo 19
Debug/Technical mode 19
Explore things in debug mode 19
Activity Area 20
Custom Module (Changes in existing module) 21
Steps to know information about Views(current page) 22
First custom module 24
Exercise 1 24
Informations on url 26
Exercise 2 26
Exercise 3 27
Exercise 4 28
Develop your own module (A New Application) 28
Make your own class 28
Security 29
Access Right 30
Ir.model.access.csv 30
Types of field 31
Attributes of field 32
Form View and Tree View 33
Upgrade and restart 36
A New Application 36
Task 1 39
Task 2 39
Task 3 39
Task 4 40
Task 5 41
Finally, Some UI To Play With 41
Data Files (XML) 41
Task 6 42
Menus 43
Task 7 45
Fields, Attributes And View 45
Some New Attributes 46
Task 8 46
Default Values 46
Task 9 47
Reserved Fields 47
A few field names are reserved for pre-defined behaviors. They should be defined on a
model when the related behavior is desired. 47
Task 10 47
Task 11 48
Task 12 48
Basic Views 49
List 49
Task 13 50
Form 50
It is possible to use regular HTML tags such as div and h1 as well as the the class
attribute (Odoo provides some built-in classes) to fine-tune the look. 51
Task 14 51
Search 52
Reference: the documentation related to this topic can be found in Search. 52
Task 15 53
Domains 54
Task 16 54
Relations Between Models 55
Many2one 55
Task 17 56
Many2many 58
Task 18 59
One2many 60
Task 19 61
Computed Fields And Onchanges 62
Computed Fields 62
Dependencies 64
Task 20 65
Task 21 65
Inverse Function 65
Task 22 66
Additional Information 67
Onchanges 68
Task 23 69
Ready For Some Action? 69
Action Type 70
Task 24 73
Object Type 73
Constraints 73
SQL 74
Task 25 75
Python 75
Task 26 77
Add The Sprinkles 77
Inline Views 78
Task 27 79
Widgets 79
Task 28 80
List Order 81
Model 81
Task 29 82
View 82
Manual 82
Task 30 83
Attributes and options 83
Task 31 84
Task 32 85
Task 33 86
Task 34 86
Task 35 87
Search 88
Task 36 88
Task 37 89
Stat Buttons(Smart Button) 89
Task 38 90
Inheritance 91
Task 39 94
Model Inheritance 94
Task 40 95
View Inheritance 95
Task 41 97
Interact With Other Modules 98
Concrete Example: Account Move 98
Link Module 99
Task 42 99
Invoice Creation 99
Task 43 100
Task 44 100
Task 45 102
A Brief History Of QWeb 102
Concrete Example: A Kanban View 102
Task 46 104
Task 47 105
Task 48 105
The final word 105
Coding guidelines 105
Task 49 105
Test on the runbot 106
Task 50 106
Build PDF Reports 106
File Structure 106
Basic Report 107
Report Data 108
Minimal Template 109
Report Action 111
Make a Report 112
Task 112
Sub-templates 112
Task 113
Report Inheritance 114
Task 114
Additional Features 115
Translations 115
Reports are web pages 115
Barcodes 115
Miscellaneous 116
Grep 116
Runbot 116
Github 117
Induction to Odoo
Odoo is an open-source business management software that offers a wide range of
integrated applications to help businesses streamline their operations. It provides a
comprehensive suite of tools for various business functions, such as accounting,
inventory management, sales, project management, human resources, and more.
At its core, Odoo is designed to centralize and automate business processes, making
them more efficient and less time-consuming. It acts as a single platform where all
important data and activities can be managed, eliminating the need for separate
systems or manual processes.
3. User-Friendly Interface: Odoo provides a user-friendly interface that makes it easy for
users to navigate and access the different modules and features. The interface is
designed to be intuitive, allowing users to quickly learn and use the software without
extensive technical knowledge.
4. Automation and Streamlining: One of the key benefits of Odoo is its ability to
automate and streamline business processes. By digitizing and centralizing data, it
eliminates manual tasks and reduces the risk of errors. For example, with Odoo,
businesses can automate accounting processes, track inventory in real-time, manage
customer interactions, generate reports, and much more.
6. Open Source and Community: Odoo is an open-source software, which means its
source code is freely available and can be modified by users. This openness
encourages collaboration and innovation within the community. The Odoo community
actively contributes to the software's development by creating new modules, fixing bugs,
and sharing knowledge and best practices.
Architecture Overview
Multitier application
Odoo follows a multitier architecture, meaning that the presentation, the business logic
and the data storage are separated. More specifically, it uses a three-tier architecture.
The presentation tier is a combination of HTML5, JavaScript and CSS. The logic tier is
exclusively written in Python, while the data tier only supports PostgreSQL as an
RDBMS.
Depending on the scope of your module, Odoo development can be done in any of
these tiers. Therefore, before going any further, it may be a good idea to refresh your
memory if you don’t have an intermediate level in these topics.
Versions of odoo
There are several versions of the odoo like version 12, 13, 14, 15 and 16 the latest
version is odoo version16.0
Before you start doing work, you must know which version of odoo you are using.
Odoo modules
Both server and client extensions are packaged as modules which are optionally loaded
in a database. A module is a collection of functions and data that target a single
purpose.
Odoo modules can either add brand new business logic to an Odoo system or alter and
extend existing business logic. One module can be created to add your country’s
accounting rules to Odoo’s generic accounting support, while a different module can
add support for real-time visualization of a bus fleet.
Everything in Odoo starts and ends with modules.
Terminology: developers group their business features in Odoo modules. The main
user-facing modules are flagged and exposed as Apps, but a majority of the modules
aren’t Apps. Modules may also be referred to as addons and the directories where the
Odoo server finds them from the addons_path.
Composition of a module
An Odoo module can contain a number of elements:
Business objects
A business object (e.g. an invoice) is declared as a Python class. The fields defined in
these classes are automatically mapped to database columns thanks to the ORM layer.
Object views
Define UI display
Data files
XML or CSV files declaring the model data:
● views or reports,
● configuration data (modules parametrization, security rules),
● demonstration data
● and more
Web controllers
Handle requests from web browsers
Static web data
Images, CSS or JavaScript files used by the web interface or website
None of these elements are mandatory. Some modules may only add data files (e.g.
country-specific accounting configuration), while others may only add business objects.
During this training, we will create business objects, object views and data files.
Module structure
Each module is a directory within a module directory. Module directories are specified
by using the --addons-path option.
An Odoo module is declared by its manifest.
When an Odoo module includes business objects (i.e. Python files), they are organized
as a Python package with a __init__.py file. This file contains import instructions for
various Python files in the module.
Now you will see the page like this mentioned below
In the above picture two things are highlighted with red, when you click on the 16.0, you
will find the various version of odoo, select the version 16.0,
Then on the same page code is highlighted, click on that, after clicking on that you will
see download zip option, click on download zip.
4. Now you’ll have a odoo-16.0.zip in your downloads.
5. Copy that zip file, and paste it in your workspace which you have created,
6. Then extract that folder(Right click to that folder>> Click on extract here)
7. Now open the odoo-16.0 folder, check the various files and folders of it.
There is a purpose of each file and folders into it, we’ll get to about them step by step.
If your terminal showing the Error message as mentioned in the given picture than use:
/usr/bin/python3.8 before ./odoo-bin
In this page you will notice the Master password, which is auto generated, but
change that master password with name supervisor351 , this master password
you have to remember always. As this master password will later be required
for database operations like delete, duplicate,etc
3. Database name should always be same as what you have kept for db-filter in this
case it is trainingdb
4. Email could be anything, but keep it simple as it will be required for further
login(ex-admin)
5. Password could be anything(ex- admin123). It will also be required for further
login.
6. Phone number: enter any phone number
7. Language: select English(US)
8. Country: select United States
9. Check the demo data checkbox (demo data will create some demo records to the
database, if you don’t create demo database you will get an empty database
without existing records)
10. Click on create Database button
Now after completing details your page will be similar to the given picture
Home Menu
In the top left corner you will see the nine dots that is the home menu, once you click
that you will be shown your apps, but for now you will be shown apps and settings only.
Apps
As you can see after clicking on apps you will have the same screen.
Menu items
Every app has their own menuitems. These are the left topmost menus you can see
after clicking on apps.
Explore sale
Go to the home menu, after installing/activating sales you will notice some more apps
like Discuss, sales, Dashboard, invoicing to your home menu, this is because sales
apps having a dependency on these apps
1. Go to home menu and click on sales apps
2. In the mentioned image Orders, To Invoice, Products, Reporting and
Configuration are known as Menuitems.
3. On Orders menu you will see the following sub menus
a. Quotations
b. Orders
c. Sales Teams
d. Customers
4. Click on quotations
5. These are some demo records, this view is known as tree view or list view.
8. In the above image you can see the different fields labeled as Customer, Order
Date, Pricelist, Quotation Template. Also Orderlines and Other Info are the tabs
on that form.
9. On the Order lines tab, there are also some fields you can see named as
Product, Description, Quantity, Delivered,etc
10. In the above picture left highlighted with red are the buttons present on the form
11. At right side upper highlighted are the stages
12. And at lower right side selected with red is Smart button
13. Now when you click on the Create invoice button, a pop up will appear on the
screen that screen is known as wizard
14. You can print the pdf of your quotation order after clicking on Print
15. You can duplicate your current record after clicking on action at the top-right.
Duplicate means creating a new record with copied data of the current existing
record.
16. Now perform the following task by yourself
a. Create a new quotations by yourself containing one product with 3
quantities of that product
b. Now duplicate that record.
c. Print a quotation order for that record
d. Now create a sale order (You can create a sale order in two ways i) by
confirming quotations or directly create a sale order)
Stop odoo local server
1. To stop your machine’s odoo server go to the terminal from where you are
running odoo.
2. Now press ctrl+c.
3. When your odoo is stopped, now when you go the browser and enter
localhost:8082, your page will like as mentioned in the below screenshot
Restart odoo
1. To restart odoo press the upward arrow key, which will bring you to the latest
command you have entered.
2. And then hit enter key
Debug/Technical mode
Debug mode or technical mode is used by the developers or technical team to know the
details about any fields like its technical name, relations, datatype or many more field
details, or you can also check the buttons’ method name.
How to open technical view
1. Go to your url.
Now your url is like this as mentioned in the image, put your cursor just before #
present in your url.
Activity Area
The highlighted area with red color in the given picture is known as the activity area
In this activity area logs are created
Great!, You reached here, you have successfully understood these concepts 🌟
Now you are ready for the customization. 👍
Custom Module (Changes in existing module)
To customize the module you have to make your own custom modules.
Module structure
Each module is a directory within a module directory. Module directories are specified
by using the --addons-path option.
When an Odoo module includes business objects (i.e. Python files), they are organized
as a Python package with a __init__.py file. This file contains import instructions for
various Python files in the module.
module
├── models
│ ├── *.py
│ └── __init__.py
├── data
│ └── *.xml
├── security
│ └── ir.model.access.csv
├── __init__.py
└── __manifest__.py
1. Module Directory:
● The root directory of your module.
● The name of this directory should follow the Odoo module naming
convention.
2. __manifest__.py:
● The main configuration file for your module.
● Contains metadata and dependencies information.
● Specifies the module name, version, summary, author, dependencies, and
other module-specific details.
3. models Directory:
● Contains Python files that define the custom models and business logic of
your module.
● Each model is typically defined in a separate Python file.
● You'll inherit from Odoo's base models to extend or create new models
specific to your module.
● You'll define fields, methods, relationships, and other logic in these files.
4. views Directory:
● Contains XML files that define the user interface components for your
module.
● Each view is defined in a separate XML file.
● Views specify the layout, fields, and actions associated with a specific
model.
● Different types of views include form views, list views, kanban views, and
calendar views.
5. security Directory:
● Contains XML files that define access control rules for your module.
● Each security file defines access rules for specific models and actions.
● You can specify permissions for different user groups, including read,
write, create, and delete operations.
6. data Directory:
● Contains XML files for pre-defined data and initial configurations.
● You can use these files to create or update records during module
installation.
7. Other Files:
● Depending on your module's functionality, you may have additional files or
directories.
● Examples include localization files, documentation files, and demo data
files.
Remember that this is a general structure, and you can customize it based on your
module's specific requirements. It's also a good practice to follow Odoo's module
Exercise 1
Add a field below Customer in form view of sale.order model, field type should be char
field, label should be Alternate customer Name, technical name should be
alt_customer_name
Make your own custom module by following the mentioned steps
1. Inside your workspace, where you have put odoo addons, make a folder named
with my_custom_addons
2. Inside my_custom_addons, make a custom module named like my_module
3. Now in my_module make a manifest file
__menifest__.py
{
'name': 'My Module', # app name which is visible
'version': '1.0',
'summary': 'this module is for training purpose',
'author': 'Your Name',
'depends': ['base','sale'], #dependent base module
'data': ['views/training_view.xml'], #xml files path
'installable': True,
'application': True,
}
Informations on url
Check the url: url is showing the various information about the page
Exercise 2
Find out the model of the given screen sort
Exercise 3
Perform the following task for account.move(model) in account.move.form
1. Add a field after payment terms named with payment_date, labeled with Payment
Date, field type is datetime.
2. Add a field after quantity in Invoice lines, field name is discount_price, labeled
with Discount Price, type is float
Exercise 4
Perform the following task for account.move(model) in account.move.tree
3. Add a field after Tax Excluded in account.move list view, field name is
discount_price, labeled with Discount Price, type is float
Python file
from odoo import models, fields
3. Define your class: Create a class that extends one of the Odoo base model
classes. For example, if you're creating a new data model, extend
odoo.models.Model. Provide a meaningful name for your class that reflects the
purpose of the model
Python file
class MyCustomClass(models.Model):
_name = 'my.custom.class'
_description = 'Description of my custom class'
# Fields definition
field1 = fields.Char(string='Field 1')
field2 = fields.Integer(string='Field 2')
# Methods definition
def my_method(self):
# Method implementation
pass
4. Define class attributes: Inside your class, you can define various attributes such
as fields, relationships, and methods.
a. Fields: Define fields using the appropriate field types provided by the
odoo.fields module. Specify the field name, field type, and other attributes
such as labels, help texts, and default values. Common field types include
Char, Integer, Boolean, Selection, Many2one, One2many, and
Many2many.
b. Relationships: If your class needs to establish relationships with other
models, you can define related fields. For example, you can create a
Many2one field to establish a many-to-one relationship with another
model.
c. Methods: Define methods within your class to implement the desired
functionality. You can create methods for data processing, validations,
calculations, and other operations.
5. Register your class in the module's __init__.py file: Open the __init__.py file in
your module's directory and add an import statement for your class. This ensures
that your class is loaded when the module is installed.
6. Restart and upgrade your module: Restart the Odoo server and upgrade your
custom module from the Odoo user interface. This will make your class available
in the system.
Now you can utilize your custom class within your module or other modules by creating
instances, accessing fields, and invoking methods as needed.
Security
In the previous section, we learned to create our first table intended to store business
data. In a business application such as Odoo, one of the first questions to consider is
who (odoo user or group of users) can access the data. Odoo provides a security
mechanism to allow access to the data for specific groups of users.
Access Right
Access rights are defined as records of the model ir.model.access. Each access right is
associated with a model, a group (or no group for global access) and a set of
permissions: create, read, write and unlink. Such access rights are usually defined in a
CSV file named ir.model.access.csv.
Ir.model.access.csv
In Odoo, the `ir.model.access.csv` file is used to define access control rules for models.
It is a CSV (Comma-Separated Values) file that allows you to specify which user groups
have read, write, create, and delete access to specific models in Odoo. The
`ir.model.access.csv` file is typically located in the module's `security` directory.
Here's an example of how to define access control rules using the `ir.model.access.csv`
file:
1. Create the `ir.model.access.csv` file: Inside your module's `security` directory, create
a file named `ir.model.access.csv`.
2. Define access control rules: In the `ir.model.access.csv` file, you can specify the
access control rules using the following columns:
Here's an example of an `ir.model.access.csv` file that grants read and write access to
the Sales Manager group for the "sale.order" model:
```csv file
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_my_custom_class,my_custom_class,model_my_custom_class,base.group_use
r,1,0,0,0
```
3. Update the module's manifest file: Open your module's `__manifest__.py` file and
make sure the `security/ir.model.access.csv` file is included in the `data` list. This
ensures that the access control rules are loaded when the module is installed.
```python
'data': [
'security/ir.model.access.csv',
# Other module data files
],
```
4. Restart and upgrade the module: Restart the Odoo server and upgrade your module
from the Odoo user interface. This will apply the access control rules defined in the
`ir.model.access.csv` file.
By defining access control rules in the `ir.model.access.csv` file, you can control the
permissions granted to user groups for specific models in Odoo. It's important to
regularly review and update the access control rules to ensure the appropriate level of
security and data protection.
Types of field
There are various field types available for defining attributes in models. Each field type
represents a specific type of data that can be stored in the database. Here are some of
the commonly used field types in Odoo 16:
1. Char: Represents a single line of text. It is used for storing short strings or names.
2. Text: Represents a multi-line text field. It is used for storing longer text content such
as descriptions or notes.
13. HTML: Represents HTML-formatted text. It can be used to store rich text content.
Attributes of field
Fields have various attributes that can be defined to customize their behavior and
appearance. These attributes are specified as parameters when defining a field in a
model class. Here are some commonly used attributes for fields in Odoo 16:
1. string: Specifies the label or display name of the field in the user interface.
2. help: Provides a help text or tooltip for the field, giving additional information or
instructions to the user.
5. default: Sets a default value for the field. It can be a constant value or a callable
method.
6. compute: Specifies a method that calculates the value of the field dynamically based
on other fields or conditions. The method should be decorated with `@api.depends` and
should return the computed value.
7. inverse: Specifies a method that is triggered when the value of the field is changed. It
allows you to perform additional actions or updates based on the field value. The
method should be decorated with `@api.onchange` or `@api.depends`.
8. domain: Defines a domain filter for the field, allowing you to restrict the selectable
options based on specific criteria.
9. related: Specifies a related field from another model. It allows you to display or
retrieve information from a related model based on the value of the current field.
10. size: Specifies the maximum length or size of the field's value, such as for Char or
Text fields.
11. digits: Specifies the precision (number of decimal places) for numeric fields, such as
Float or Monetary fields.
12. selection: Defines a list of options for a Selection field. It specifies the available
choices for the field.
13. groups: Specifies the user groups that are granted access to the field. It restricts the
visibility or editability of the field based on user group membership.
14. ondelete: Specifies the behavior when the referenced record of a Many2one or
One2many field is deleted. It determines whether to restrict, set to NULL, or cascade
the deletion.
2. Define the form view XML structure: Open the `my_model_form_view.xml` file and
define the XML structure using the `<odoo>`, `<data>`, and `<record>` tags. For
example:
```xml
<odoo>
<data>
<record id="my_model_form_view" model="ir.ui.view">
<field name="name">my.model.form.view</field>
<field name="model">my.model</field>
<field name="arch" type="xml">
<!-- Form view definition goes here -->
</field>
</record>
</data>
</odoo>
```
3. Define the form view structure: Inside the `<record>` tag, define the form view
structure using the `<form>` tag. Specify the view's attributes such as `string` for the
view's title and `create`, `edit`, and `delete` for the view's access rights. For example:
```xml
<form string="My Model Form" create="true" edit="true" delete="true">
<!-- Fields and other elements of the form view go here -->
</form>
```
4. Add fields and elements to the form view: Within the `<form>` tag, define the fields
and other elements that you want to display in the form view. Use the appropriate field
tags such as `<field>`, `<group>`, or `<page>` to organize the layout and structure of
the form. For example:
```xml
<form string="My Model Form" create="true" edit="true" delete="true">
<sheet>
<group>
<field name="name"/>
<field name="description"/>
</group>
</sheet>
</form>
```
5. Define the tree view XML structure: Open the `my_model_tree_view.xml` file and
define the XML structure similar to the form view. For example:
```xml
<odoo>
<data>
<record id="my_model_tree_view" model="ir.ui.view">
<field name="name">my.model.tree.view</field>
<field name="model">my.model</field>
<field name="arch" type="xml">
<!-- Tree view definition goes here -->
</field>
</record>
</data>
</odoo>
```
6. Define the tree view structure: Inside the `<record>` tag, define the tree view
structure using the `<tree>` tag. For example:
```xml
<tree string="My Model Tree">
<!-- Fields and other elements of the tree view go here -->
</tree>
```
7. Add fields and elements to the tree view: Within the `<tree>` tag, define the fields and
other elements that you want to display in the tree view. Use the `<field>` tag to display
fields from the model. For example:
```xml
<tree string="My Model Tree">
<field name="name"/>
<field name="description"/>
</tree>
```
8. Update the module's manifest file: Open your module's `__manifest__.py` file and
make sure to include both XML files in the `data` list. This ensures that the form and
tree views are loaded when the module is installed or upgraded.
-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.
A New Application
Our new module will cover a business area which is very specific and therefore not
included in the standard set of modules: real estate. It is worth noting that before
developing a new module, it is good practice to verify that Odoo doesn’t already provide
a way to answer the specific business case.
The top area of the form view summarizes important information for the property, such
as the name, the property type, the postcode and so on. The first tab contains
information describing the property: bedrooms, living area, garage, garden…
The second tab lists the offers for the property. We can see here that potential buyers
can make offers above or below the expected selling price. It is up to the seller to
accept an offer.
Goal: the goal of this section is to have Odoo recognize our new module, which
will be an empty shell for now. It will be listed in the Apps:
Task 2
Task 3
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.
Hint: As you know till now how to create a field in odoo
garden = fields.Boolean(string="Garden")
When the fields are added to the model, restart the server with -u estate
Task 4
Set attributes for existing fields.
Add the following attributes:
Field Attribute
name required
expected_price required
In Odoo, the user interface (actions, menus and views) is largely defined by creating
and composing records defined in an XML file. A common pattern is Menu > Action >
View. To access records the user navigates through several menu levels; the deepest
level is an action which triggers the opening of a list of the records.
Actions can be triggered in three ways:
For now we will see action as contextual actions on objects. In our Real Estate example,
we would like to link a menu to the estate.property model, so we are able to create
a new record. The action can be viewed as the link between the menu and the model.
Examples can be found everywhere in Odoo, but this is a good example of a simple
action. Pay attention to the structure of the XML data file since you will need it in the
following exercise.
Task 6
Add an action.
Create the estate_property_views.xml file in the appropriate folder and define it in the
__manifest__.py file.
Create an action for the model estate.property.
Menus
Goal: at the end of this section, three menus should be created and the default view is
displayed:
After you click in real estate application, your screen view should be like as mentioned
below:
However, menus always follow an architecture, and in practice there are three levels of
menus:
1. The root menu, which is displayed in the App switcher (the Odoo Community App
switcher is a dropdown menu)
2. The first level menu, displayed in the top bar
3. The action menus
The easiest way to define the structure is to create it in the XML file. A basic structure
for our test_model_action is:
Task 7
Add menus.
Create the estate_menus.xml file in the appropriate folder and define it in the
__manifest__.py file. Remember the sequential loading of the data files.
Create the three levels of menus for the estate.property action created in the previous
exercise. Refer to the Goal of this section for the expected result.
Restart the server and refresh the browser. You should now see the menus, and you’ll
even be able to create your first real estate property advertisement!
The reserved fields active and state are added to the estate.property model.
So far we have only used the generic view for our real estate property advertisements,
but in most cases we want to fine tune the view. There are many fine-tunings possible in
Odoo, but usually the first step is to make sure that:
● The selling price should be read-only (it will be automatically filled in later)
● The availability date and the selling price should not be copied when duplicating
a record
● The default number of bedrooms should be 2
● The default availability date should be in 3 months
Task 8
Add new attributes to the fields.
Find the appropriate attributes (see Field) to:
● set the selling price as read-only
● prevent copying of the availability date and the selling price values
Restart the server and refresh the browser. You should not be able to set any selling
prices. When duplicating a record, the availability date should be empty.
Default Values
Any field can be given a default value. In the field definition, add the option default=X
where X is either a Python literal value (boolean, integer, float, string) or a function taking
a model and returning a value:
Example
name = fields.Char(default="Unknown")
last_seen = fields.Datetime("Last Seen", default=lambda self: fields.Datetime.now())
The name field will have the value ‘Unknown’ by default while the last_seen field will
be set as the current time.
Task 9
Set default values.
Add the appropriate default attributes so that:
● the default number of bedrooms is 2
● the default availability date is in 3 months
Tip: this might help you: today()
Reserved Fields
A few field names are reserved for pre-defined behaviors. They should be defined on a
model when the related behavior is desired.
Task 10
Add active field.
Add the active field to the estate.property model.
Restart the server, create a new property, then come back to the list view… The property
will not be listed! active is an example of a reserved field with a specific behavior:
when a record has active=False, it is automatically removed from any search. To
display the created property, you will need to specifically search for inactive records.
Task 11
Set a default value for active field.
Set the appropriate default value for the active field so it doesn’t disappear anymore.
Note that the default active=False value wa all existing records.s assigned to
Task 12
Add state field.
Add a state field to the estate.property model. Five values are possible: New, Offer
Received, Offer Accepted, Sold and Canceled. It must be required, should not be copied
and should have its default value set to ‘New’.
Make sure to use the correct type!
Now that we are able to interact with the UI thanks to the default views, the next step is
obvious: we want to define our own views.
Basic Views
We have seen in the previous section that Odoo is able to generate default views for a
given model. In practice, the default view is never acceptable for a business application.
Instead, we should at least organize the various fields in a logical manner.
Views are defined in XML files with actions and menus. They are instances of the
ir.ui.view model.
In our real estate module, we need to organize the fields in a logical way:
● in the list (tree) view, we want to display more than just the name.
● in the form view, the fields should be grouped.
● in the search view, we must be able to search on more than just the name.
Specifically, we want a filter for the ‘Available’ properties and a shortcut to group
by postcode.
List
The documentation related to this topic can be found in List.
Note
Goal: at the end of this section, the list view should look like this:
List views, also called tree views, display records in a tabular form.
Their root element is <tree>. The most basic version of this view simply lists all the
fields to display in the table (where each field is a column):
<tree string="Tests">
<field name="name"/>
<field name="last_seen"/>
</tree>
To take reference you can check any of the odoo base module code of view
Task 13
Add a custom list view.
Define a list view for the estate.property model in the appropriate XML file. Check the
Goal of this section for the fields to display.
Tips:
● do not add the editable="bottom" attribute that you can find in the example
above. We’ll come back to it later.
● some field labels may need to be adapted to match the reference.
As always, you need to restart the server (do not forget the -u option) and refresh the
browser to see the result.
Warning
You will probably use some copy-paste in this chapter, therefore always make sure that
the id remains unique for each view!
Form
Reference: the documentation related to this topic can be found in Form.
Note
Goal: at the end of this section, the form view should look like this:
Forms are used to create and edit single records.
Their root element is <form>. They are composed of high-level structure elements
(groups and notebooks) and interactive elements (buttons and fields):
<form string="Test">
<sheet>
<group>
<group>
<field name="name"/>
</group>
<group>
<field name="last_seen"/>
</group>
<notebook>
<page string="Description">
<field name="description"/>
</page>
</notebook>
</group>
</sheet>
</form>
It is possible to use regular HTML tags such as div and h1 as well as the the class
attribute (Odoo provides some built-in classes) to fine-tune the look.
Task 14
Add a custom form view.
Define a form view for the estate.property model in the appropriate XML file. Check
the Goal of this section for the expected final design of the page.
This might require some trial and error before you get to the expected result ;-) It is
advised that you add the fields and the tags one at a time to help understand how it
works.
In order to avoid relaunching the server every time you do a modification to the view, it
can be convenient to use the --dev xml parameter when launching the server:
This parameter allows you to just refresh the page to view your view modifications.
Search
Reference: the documentation related to this topic can be found in Search.
Note
Goal: at the end of this section, the search view should look like this:
Search views are slightly different from the list and form views since they don’t display
content. Although they apply to a specific model, they are used to filter other views’
content (generally aggregated views such as List). Beyond the difference in use case,
they are defined the same way.
Their root element is <search>. The most basic version of this view simply lists all the
fields for which a shortcut is desired:
<search string="Tests">
<field name="name"/>
<field name="last_seen"/>
</search>
The default search view generated by Odoo provides a shortcut to filter by name. It is
very common to add the fields which the user is likely to filter on in a customized search
view.
After restarting the server, it should be possible to filter on the given fields.
Search views can also contain <filter> elements, which act as toggles for predefined
searches. Filters must have one of the following attributes:
The default search view generated by Odoo provides a shortcut to filter by name. It is very
common to add the fields which the user is likely to filter on in a customized search
view.
Task 15
Add a custom search view.
Define a search view for the estate.property model in the appropriate XML file. Check
the first image of this section’s Goal for the list of fields.
After restarting the server, it should be possible to filter on the given fields.
Search views can also contain <filter> elements, which act as toggles for predefined
searches. Filters must have one of the following attributes:
Before going further in the exercise, it is necessary to introduce the ‘domain’ concept.
Domains
Reference: the documentation related to this topic can be found in Search domains.
For instance, when used on the Product model the following domain selects all services
with a unit price greater than 1000:
By default criteria are combined with an implicit AND, meaning every criterion needs to
be satisfied for a record to match a domain. The logical operators & (AND), | (OR) and !
(NOT) can be used to explicitly combine criteria. They are used in prefix position (the
operator is inserted before its arguments rather than between). For instance, to select
products ‘which are services OR have a unit price which is NOT between 1000 and
2000’:
['|',
'!', '&',
Task 16
Add filter and Group By.
The following should be added to the previously created search view:
● a filter which displays available properties, i.e. the state should be ‘New’ or ‘Offer
Received’.
● the ability to group results by postcode.
Looking good? At this point we are already able to create models and design a user
interface which makes sense business-wise. However, a key component is still missing:
the link between models.
In our real estate module, we want the following information for a property:
Many2one
Reference: the documentation related to this topic can be found in Many2one.
Note
In our real estate module, we want to define the concept of property type. A property
type is, for example, a house or an apartment. It is a standard business need to
categorize properties according to their type, especially to refine filtering.
A property can have one type, but the same type can be assigned to many properties.
This is supported by the many2one concept.
A many2one is a simple link to another object. For example, in order to define a link to
the res.partner in our test model, we can write:
By convention, many2one fields have the _id suffix. Accessing the data in the partner
can then be easily done with:
print(my_test_object.partner_id.name)
Task 17
Add the Real Estate Property Type table.
● Create the estate.property.type model and add the following field:
Field Type Attributes
Once again, restart the server and refresh to see the results!
In the real estate module, there are still two missing pieces of information we want on a
property: the buyer and the salesperson. The buyer can be any individual, but on the
other hand the salesperson must be an employee of the real estate agency (i.e. an Odoo
user).
Many2many
Reference: the documentation related to this topic can be found in Many2many.
Note
Goal: at the end of this section:
● a new estate.property.tag model should be created with the corresponding
menu and action.
A property can have many tags and a tag can be assigned to many properties. This is
supported by the many2many concept.
By convention, many2many fields have the _ids suffix. This means that several taxes
can be added to our test model. It behaves as a list of records, meaning that accessing
the data must be done in a loop:
print(tax.name)
Task 18
Add the Real Estate Property Tag table.
● Create the estate.property.tag model and add the following field:
Field Type Attributes
One2many
Reference: the documentation related to this topic can be found in One2many.
Note
Goal: at the end of this section:
● a new estate.property.offer model should be created with the corresponding
form and tree view.
● offers should be added to the estate.property model:
In our real estate module, we want to define the concept of property offers. A property
offer is an amount a potential buyer offers to the seller. The offer can be lower or higher
than the expected price.
An offer applies to one property, but the same property can have many offers. The
concept of many2one appears once again. However, in this case we want to display the
list of offers for a given property so we will use the one2many concept.
A one2many is the inverse of a many2one. For example, we defined on our test model a
link to the res.partner model thanks to the field partner_id. We can define the inverse
relation, i.e. the list of test models linked to our partner:
The first parameter is called the comodel and the second parameter is the field we want
to inverse.
By convention, one2many fields have the _ids suffix. They behave as a list of records,
meaning that accessing the data must be done in a loop:
print(test.name)
Danger
Because a One2many is a virtual relationship, there must be a Many2one field defined in
the comodel.
Task 19
Add the Real Estate Property Offer table.
● Create the estate.property.offer model and add the following fields:
price Float
There are several important things to notice here. First, we don’t need an action or a
menu for all models. Some models are intended to be accessed only through another
model. This is the case in our exercise: an offer is always accessed through a property.
Second, despite the fact that the property_id field is required, we did not include it in
the views. How does Odoo know which property our offer is linked to? Well that’s part of
the magic of using the Odoo framework: sometimes things are defined implicitly. When
we create a record through a one2many field, the corresponding many2one is populated
automatically for convenience.
Still alive? This chapter is definitely not the easiest one. It introduced a couple of new
concepts while relying on everything that was introduced before. This section needs
some practice. )
These cases are supported by the concepts of computed fields and onchanges.
Although this chapter is not technically complex, the semantics of both concepts is very
important. This is also the first time we will write Python logic. Until now we haven’t
written anything other than class definitions and field declarations.
Computed Fields
Reference: the documentation related to this topic can be found in Computed Fields.
Note
Goal: at the end of this section:
● In the property model, the total area and the best offer should be computed:
Note
Goal: at the end of this section:
● In the property model, the total area and the best offer should be computed:
In our real estate module, we have defined the living area as well as the garden area. It is
then natural to define the total area as the sum of both fields. We will use the concept of
a computed field for this, i.e. the value of a given field will be computed from the value
of other fields.
So far fields have been stored directly in and retrieved directly from the database. Fields
can also be computed. In this case, the field’s value is not retrieved from the database
but computed on-the-fly by calling a method of the model.
To create a computed field, create a field and set its attribute compute to the name of a
method. The computation method should set the value of the computed field for every
record in self.
By convention, compute methods are private, meaning that they cannot be called from
the presentation tier, only from the business tier. Private methods have a name starting
with an underscore _.
Dependencies
The value of a computed field usually depends on the values of other fields in the
computed record. The ORM expects the developer to specify those dependencies on the
compute method with the decorator depends(). The given dependencies are used by
the ORM to trigger the recomputation of the field whenever some of its dependencies
have been modified:
class TestComputed(models.Model):
_name = "test.computed"
total = fields.Float(compute="_compute_total")
amount = fields.Float()
@api.depends("amount")
def _compute_total(self):
for record in self:
record.total = 2.0 * record.amount
Note
self is a collection.
The object self is a recordset, i.e. an ordered collection of records. It supports the
standard Python operations on collections, e.g. len(self) and iter(self), plus extra
set operations such as recs1 | recs2.
Iterating over self gives the records one by one, where each record is itself a collection
of size 1. You can access/assign fields on single records by using the dot notation, e.g.
record.name.
Many examples of computed fields can be found in Odoo. Here is a simple one.
Task 20
Compute the total area.
● Add the total_area field to estate.property. It is defined as the sum of the
living_area and the garden_area.
● Add the field in the form view as depicted on the first image of this section’s
Goal.
description = fields.Char(compute="_compute_description")
partner_id = fields.Many2one("res.partner")
@api.depends("partner_id.name")
def _compute_description(self):
for record in self:
record.description = "Test for partner %s" % record.partner_id.name
The example is given with a Many2one, but it is valid for Many2many or a One2many. An
example can be found here.
Task 21
Compute the best offer.
● Add the best_price field to estate.property. It is defined as the highest (i.e.
maximum) of the offers’ price.
● Add the field to the form view as depicted in the first image of this section’s Goal.
Tip: you might want to try using the mapped() method. See here for a simple example.
Inverse Function
You might have noticed that computed fields are read-only by default. This is expected
since the user is not supposed to set a value.
In some cases, it might be useful to still be able to set a value directly. In our real estate
example, we can define a validity duration for an offer and set a validity date. We would
like to be able to set either the duration or the date with one impacting the other.
class TestComputed(models.Model):
_name = "test.computed"
@api.depends("amount")
def _compute_total(self):
for record in self:
record.total = 2.0 * record.amount
def _inverse_total(self):
for record in self:
record.amount = record.total / 2.0
A compute method sets the field while an inverse method sets the field’s dependencies.
Note that the inverse method is called when saving the record, while the compute
method is called at each change of its dependencies.
Task 22
Compute a validity date for offers.
● Add the following fields to the estate.property.offer model:
validity Integer 7
date_deadline Date
Where date_deadline is a computed field which is defined as the sum of two fields
from the offer: the create_date and the validity. Define an appropriate inverse
function so that the user can set either the date or the validity.
Tip: the create_date is only filled in when the record is created, therefore you will need
a fallback to prevent crashing at time of creation.
● Add the fields in the form view and the list view as depicted on the second image
of this section’s Goal.
Additional Information
Computed fields are not stored in the database by default. Therefore it is not possible to
search on a computed field unless a search method is defined. This topic is beyond the
scope of this training, so we won’t cover it. An example can be found here.
Another solution is to store the field with the store=True attribute. While this is usually
convenient, pay attention to the potential computation load added to your model. Lets
re-use our example:
partner_id = fields.Many2one("res.partner")
@api.depends("partner_id.name")
def _compute_description(self):
Every time the partner name is changed, the description is automatically recomputed
for all the records referring to it! This can quickly become prohibitive to recompute
when millions of records need recomputation.
It is also worth noting that a computed field can depend on another computed field. The
ORM is smart enough to correctly recompute all the dependencies in the right order…
but sometimes at the cost of degraded performance.
In general performance must always be kept in mind when defining computed fields.
The more complex is your field to compute (e.g. with a lot of dependencies or when a
computed field depends on other computed fields), the more time it will take to
compute. Always take some time to evaluate the cost of a computed field beforehand.
Most of the time it is only when your code reaches a production server that you realize it
slows down a whole process. Not cool :-(
Onchanges
Reference: the documentation related to this topic can be found in onchange():
Note
Goal: at the end of this section, enabling the garden will set a default area of 10 and an
orientation to North.
In our real estate module, we also want to help the user with data entry. When the
‘garden’ field is set, we want to give a default value for the garden area as well as the
orientation. Additionally, when the ‘garden’ field is unset we want the garden area to
reset to zero and the orientation to be removed. In this case, the value of a given field
modifies the value of other fields.
The ‘onchange’ mechanism provides a way for the client interface to update a form
without saving anything to the database whenever the user has filled in a field value. To
achieve this, we define a method where self represents the record in the form view and
decorate it with onchange() to specify which field it is triggered by. Any change you
make on self will be reflected on the form:
class TestOnchange(models.Model):
_name = "test.onchange"
name = fields.Char(string="Name")
description = fields.Char(string="Description")
partner_id = fields.Many2one("res.partner", string="Partner")
@api.onchange("partner_id")
def _onchange_partner_id(self):
self.name = "Document for %s" % (self.partner_id.name)
self.description = "Default description for %s" % (self.partner_id.name)
In this example, changing the partner will also change the name and the description
values. It is up to the user whether or not to change the name and description values
afterwards. Also note that we do not loop on self, this is because the method is only
triggered in a form view, where self is always a single record.
Task 23
Set values for garden area and orientation.
Create an onchange in the estate.property model in order to set values for the garden
area (10) and orientation (North) when the garden is set to True. When unset, clear the
fields.
Action Type
Reference: the documentation related to this topic can be found in Actions and Error
management.
Note
Goal: at the end of this section:
● You should be able to cancel or set a property as sold:
A canceled property cannot be sold and a sold property cannot be canceled. For the
sake of clarity, the state field has been added on the view.
● You should be able to accept or refuse an offer:
● Once an offer is accepted, the selling price and the buyer should be set:
In our real estate module, we want to link business logic with some buttons. The most
common way to do this is to:
● Add a button in the view, for example in the header of the view:
<form>
<header>
<button name="action_do_something" type="object" string="Do Something"/>
</header>
<sheet>
<field name="name"/>
</sheet>
</form>
● and link this button to business logic:
class TestAction(models.Model):
_name = "test.action"
name = fields.Char()
def action_do_something(self):
for record in self:
record.name = "Something"
return True
By assigning type="object" to our button, the Odoo framework will execute a Python
method with name="action_do_something" on the given model.
The first important detail to note is that our method name isn’t prefixed with an
underscore (_). This makes our method a public method, which can be called directly
from the Odoo interface (through an RPC call). Until now, all methods we created
(compute, onchange) were called internally, so we used private methods prefixed by an
underscore. You should always define your methods as private unless they need to be
called from the user interface.
Also note that we loop on self. Always assume that a method can be called on multiple
records; it’s better for reusability.
Finally, a public method should always return something so that it can be called through
XML-RPC. When in doubt, just return True.
There are hundreds of examples in the Odoo source code. One example is this button in
a view and its corresponding Python method
Task 24
Cancel and set a property as sold.
● Add the buttons ‘Cancel’ and ‘Sold’ to the estate.property model. A canceled
property cannot be set as sold, and a sold property cannot be canceled.
Refer to the first image of the Goal for the expected result.
Tip: in order to raise an error, you can use the UserError function. There are plenty
of examples in the Odoo source code ;-)
● Add the buttons ‘Accept’ and ‘Refuse’ to the estate.property.offer model.
Refer to the second image of the Goal for the expected result.
Tip: to use an icon as a button, have a look at this example.
● When an offer is accepted, set the buyer and the selling price for the
corresponding property.
Refer to the third image of the Goal for the expected result.
Pay attention: in real life only one offer can be accepted for a given property!
Object Type
In xml files, we created an action that was linked to a menu. You may be wondering if it
is possible to link an action to a button. Good news, it is! One way to do it is:
Constraints
The previous chapter introduced the ability to add some business logic to our model.
We can now link buttons to business code, but how can we prevent users from entering
incorrect data? For example, in our real estate module nothing prevents users from
setting a negative expected price.
Task 25
Add SQL constraints.
Add the following constraints to their corresponding models:
● A property expected price must be strictly positive
● A property selling price must be positive
● An offer price must be strictly positive
● A property tag name and property type name must be unique
Tip: search for the unique keyword in the Odoo codebase for examples of unique
names.
Restart the server with the -u estate option to see the result. Note that you might have
data that prevents a SQL constraint from being set. An error message similar to the
following might pop up:
Python
Reference: the documentation related to this topic can be found in constrains()
Note
Goal: at the end of this section, it will not be possible to accept an offer lower than 90%
of the expected price.
SQL constraints are an efficient way of ensuring data consistency. However it may be
necessary to make more complex checks which require Python code. In this case we
need a Python constraint.
...
@api.constrains('date_end')
def _check_date_end(self):
for record in self:
if record.date_end < fields.Date.today():
raise ValidationError("The end date cannot be set in the past")
# all records passed the test, don't return anything
Task 26
Add Python constraints.
Add a constraint so that the selling price cannot be lower than 90% of the expected
price.
Tip: the selling price is zero until an offer is validated. You will need to fine tune your
check to take this into account.
Warning
Always use the float_compare() and float_is_zero() methods from
odoo.tools.float_utils when working with floats!
Ensure the constraint is triggered every time the selling price or the expected price is
changed!
SQL constraints are usually more efficient than Python constraints. When performance
matters, always prefer SQL over Python constraints.
Our real estate module is starting to look good. We added some business logic, and
now we make sure the data is consistent. However, the user interface is still a bit rough.
This chapter covers a very small subset of what can be done in the views. Do not
hesitate to read the reference documentation for a more complete overview.
In the real estate module we added a list of offers for a property. We simply added the
field offer_ids with:
<field name="offer_ids"/>
The field uses the specific view for estate.property.offer. In some cases we want to
define a specific list view which is only used in the context of a form view. For example,
we would like to display the list of properties linked to a property type. However, we only
want to display 3 fields for clarity: name, expected price and state.
To do this, we can define inline list views. An inline list view is defined directly inside a
form view. For example:
class TestModel(models.Model):
_name = "test.model"
_description = "Test Model"
description = fields.Char()
line_ids = fields.One2many("test.model.line", "model_id")
class TestModelLine(models.Model):
_name = "test.model.line"
_description = "Test Model Line"
model_id = fields.Many2one("test.model")
field_1 = fields.Char()
field_2 = fields.Char()
field_3 = fields.Char()
<form>
<field name="description"/>
<field name="line_ids">
<tree>
<field name="field_1"/>
<field name="field_2"/>
</tree>
</field>
</form>
In the form view of the test.model, we define a specific list view for test.model.line
with fields field_1 and field_2.
An example can be found here.
Task 27
Add an inline list view.
● Add the One2many field property_ids to the estate.property.type model.
● Add the field in the estate.property.type form view as depicted in the Goal of
this section.
Widgets
Reference: the documentation related to this section can be found in Field Widgets.
Note
Goal: at the end of this section, the state of the property should be displayed using a
specific widget:
Four states are displayed: New, Offer Received, Offer Accepted and Sold.
Whenever we’ve added fields to our models, we’ve (almost) never had to worry about
how these fields would look like in the user interface. For example, a date picker is
provided for a Date field and a One2many field is automatically displayed as a list. Odoo
chooses the right ‘widget’ depending on the field type.
However, in some cases, we want a specific representation of a field which can be done
thanks to the widget attribute. We already used it for the tag_ids field when we used
the widget="many2many_tags" attribute. If we hadn’t used it, then the field would have
displayed as a list.
Each field type has a set of widgets which can be used to fine tune its display. Some
widgets also take extra options. An exhaustive list can be found in Field Widgets
Task 28
Use the status bar widget.
Use the statusbar widget in order to display the state of the estate.property as
depicted in the Goal of this section.
Tip: a simple example can be found here.
Warning
Same field multiple times in a view
Add a field only once to a list or a form view. Adding it multiple times is not supported.
List Order
Reference: the documentation related to this section can be found in Models.
Note
Goal: at the end of this section, all lists should display by default in a deterministic
order. Property types can be ordered manually.
During the previous exercises, we created several list views. However, at no point did we
specify which order the records had to be listed in by default. This is a very important
thing for many business cases. For example, in our real estate module we would want to
display the highest offers on top of the list.
Model
Odoo provides several ways to set a default order. The most common way is to define
the _order attribute directly in the model. This way, the retrieved records will follow a
deterministic order which will be consistent in all views including when records are
searched programmatically. By default there is no order specified, therefore the records
will be retrieved in a non-deterministic order depending on PostgreSQL.
The _order attribute takes a string containing a list of fields which will be used for
sorting. It will be converted to an order_by clause in SQL. For example:
class TestModel(models.Model):
_name = "test.model"
_description = "Test Model"
_order = "id desc"
description = fields.Char()
Our records are ordered by descending id, meaning the highest comes first.
Task 29
Add model ordering.
Define the following orders in their corresponding models:
Model Order
estate.property Descending ID
estate.property.tag Name
estate.property.type Name
View
Ordering is possible at the model level. This has the advantage of a consistent order
everywhere a list of records is retrieved. However, it is also possible to define a specific
order directly in a view thanks to the default_order attribute (example).
Manual
Both model and view ordering allow flexibility when sorting records, but there is still one
case we need to cover: the manual ordering. A user may want to sort records depending
on the business logic. For example, in our real estate module we would like to sort the
property types manually. It is indeed useful to have the most used types appear at the
top of the list. If our real estate agency mainly sells houses, it is more convenient to
have ‘House’ appear before ‘Apartment’.
To do so, a sequence field is used in combination with the handle widget. Obviously the
sequence field must be the first field in the _order attribute.
Task 30
Add manual ordering.
● Add the following field:
● Add the sequence to the estate.property.type list view with the correct
widget.
Tip: you can find an example here: model and view.
Task 31
Add widget options.
● Add the appropriate option to the property_type_id field to prevent the creation
and the editing of a property type from the property form view. Have a look at the
Many2one widget documentation for more info.
● Add the following field:
Then add the appropriate option to the tag_ids field to add a color picker on the tags.
Have a look at the FieldMany2ManyTags widget documentation for more info.
We saw that reserved fields were used for specific behaviors. For example, the active
field is used to automatically filter out inactive records. We added the state as a
reserved field as well. It’s now time to use it! A state field is used in combination with a
states attribute in the view to display buttons conditionally.
The attrs is a dictionary with the property as a key and a domain as a value. The
domain gives the condition in which the property applies. For example:
<form>
<field name="description" attrs="{'invisible': [('is_partner', '=', False)]}"/>
<field name="is_partner" invisible="1"/>
</form>
This means that the description field is invisible when is_partner is False. It is
important to note that a field used in an attrs must be present in the view. If it should
not be displayed to the user, we can use the invisible attribute to hide it.
Task 32
Use attrs.
● Make the garden area and orientation invisible in the estate.property form view
when there is no garden.
● Make the ‘Accept’ and ‘Refuse’ buttons invisible once the offer state is set.
● Do not allow adding an offer when the property state is ‘Offer Accepted’, ‘Sold’ or
‘Canceled’. To do this use the read only attrs.
Warning
Using a (conditional) readonly attribute in the view can be useful to prevent data entry
errors, but keep in mind that it doesn’t provide any level of security! There is no check
done server-side, therefore it’s always possible to write on the field through a RPC call.
Note
Goal: at the end of this section, the property and offer list views should have color
decorations. Additionally, offers and tags will be editable directly in the list, and the
availability date will be hidden by default.
When the model only has a few fields, it can be useful to edit records directly through
the list view and not have to open the form view. In the real estate example, there is no
need to open a form view to add an offer or create a new tag. This can be achieved
thanks to the editable attribute.
Task 33
Make list views editable.
Make the estate.property.offer and estate.property.tag list views editable.
On the other hand, when a model has a lot of fields it can be tempting to add too many
fields in the list view and make it unclear. An alternative method is to add the fields, but
make them optionally hidden. This can be achieved thanks to the optional attribute.
Task 34
Make a field optional.
Make the field date_availability on the estate.property list view optional and
hidden by default.
Finally, color codes are useful to visually emphasize records. For example, in the real
estate module we would like to display refused offers in red and accepted offers in
green. This can be achieved thanks to the decoration-{$name} attribute (see Field
Widgets for a complete list):
<tree decoration-success="is_partner==True">
<field name="name"/>
<field name="is_partner" invisible="1"/>
</tree>
Task 35
Add some decorations.
On the estate.property list view:
● Properties with an offer received are green
● Properties with an offer accepted are green and bold
● Properties sold are muted
On the estate.property.offer list view:
● Refused offers are red
● Accepted offers are green
● The state should not be visible anymore
Tips:
● Keep in mind that all fields used in attributes must be in the view!
● If you want to test the color of the “Offer Received” and “Offer Accepted” states,
add the field in the form view and change it manually (we’ll implement the
business logic for this later).
Search
Reference: the documentation related to this section can be found in Search and Search
defaults.
Note
Goal: at the end of this section, the available properties will be filtered by default, and
searching on the living area returns results where the area is larger than the given
number.
Last but not least, there are some tweaks we would like to apply when searching. First
of all, we want to have our ‘Available’ filter applied by default when we access the
properties. To make this happen, we need to use the search_default_{$name} action
context, where {$name} is the filter name. This means that we can define which filter(s)
will be activated by default at the action level.
Here is an example of an action with its corresponding filter.
Task 36
Add a default filter.
Make the ‘Available’ filter selected by default in the estate.property action.
Another useful improvement in our module would be the ability to search efficiently by
living area. In practice, a user will want to search for properties of ‘at least’ the given
area. It is unrealistic to expect users would want to find a property of an exact living
area. It is always possible to make a custom search, but that’s inconvenient.
Search view <field> elements can have a filter_domain that overrides the domain
generated for searching on the given field. In the given domain, self represents the
value entered by the user. In the example below, it is used to search on both name and
description fields.
<search string="Test">
<field name="description" string="Name and description"
filter_domain="['|', ('name', 'ilike', self), ('description', 'ilike', self)]"/>
</search>
Task 37
Change the living area search.
Add a filter_domain to the living area to include properties with an area equal to or
greater than the given value.
At this point of the tutorial we have already seen most of the concepts to do this.
However, there is not a single solution and it can still be confusing if you don’t know
where to start from. We’ll describe a step-by-step solution in the exercise. It can always
be useful to find some examples in the Odoo codebase by looking for oe_stat_button.
The following exercise might be a bit more difficult than the previous ones since it
assumes you are able to search for examples in the source code on your own. If you are
stuck there is probably someone nearby who can help you ;-)
The exercise introduces the concept of Related fields. The easiest way to understand it
is to consider it as a specific case of a computed field. The following definition of the
description field:
...
@api.depends("partner_id.name")
def _compute_description(self):
for record in self:
record.description = record.partner_id.name
Task 38
Add a stat button to property type.
● Add the field property_type_id to estate.property.offer. We can define it as
a related field on property_id.property_type_id and set it as stored.
Thanks to this field, an offer will be linked to a property type when it’s created. You can
add the field to the list view of offers to make sure it works.
● Add the field offer_ids to estate.property.type which is the One2many
inverse of the field defined in the previous step.
● Add the field offer_count to estate.property.type. It is a computed field that
counts the number of offers for a given property type (use offer_ids to do so).
At this point, you have all the information necessary to know how many offers are linked
to a property type. When in doubt, add offer_ids and offer_count directly to the view.
The next step is to display the list when clicking on the stat button.
● Create a stat button on estate.property.type pointing to the
estate.property.offer action. This means you should use the type="action"
attribute (go back to the end of Chapter 10: Ready For Some Action? if you need
a refresher).
At this point, clicking on the stat button should display all offers. We still need to filter
out the offers.
● On the estate.property.offer action, add a domain that defines
property_type_id as equal to the active_id (= the current record, here is an
example)
Inheritance
A powerful aspect of Odoo is its modularity. A module is dedicated to a business need,
but modules can also interact with one another. This is useful for extending the
functionality of an existing module. For example, in our real estate scenario we want to
display the list of a salesperson’s properties directly in the regular user view.
But before going through the specific Odoo module inheritance, let’s see how we can
alter the behavior of the standard CRUD (Create, Retrieve, Update or Delete) methods.
As you are little familiar with this, let us see it little more details
Note
● When an offer is created, the property state should change to ‘Offer Received’
● It should not be possible to create an offer with a lower price than an existing
offer
In our real estate module, we never had to develop anything specific to be able to do the
standard CRUD actions. The Odoo framework provides the necessary tools to do them.
In fact, such actions are already included in our model thanks to classical Python
inheritance:
class TestModel(models.Model):
_name = "test.model"
_description = "Test Model"
Our class TestModel inherits from Model which provides create(), read(), write()
and unlink().
These methods (and any other method defined on Model) can be extended to add
specific business logic:
class TestModel(models.Model):
_name = "test.model"
_description = "Test Model"
...
@api.model
def create(self, vals):
# Do some business logic, modify vals...
...
# Then call super to execute the parent method
return super().create(vals)
The decorator model() is necessary for the create() method because the content of
the recordset self is not relevant in the context of creation, but it is not necessary for
the other CRUD methods.
It is also important to note that even though we can directly override the unlink()
method, you will almost always want to write a new method with the decorator
ondelete() instead. Methods marked with this decorator will be called during unlink()
and avoids some issues that can occur during uninstalling the model’s module when
unlink() is directly overridden.
In Python 3, super() is equivalent to super(TestModel, self). The latter may be
necessary when you need to call the parent method with a modified recordset.
Danger
● It is very important to always call super() to avoid breaking the flow. There are
only a few very specific cases where you don’t want to call it.
● Make sure to always return data consistent with the parent method. For example,
if the parent method returns a dict(), your override must also return a dict().
Task 39
Add business logic to the CRUD methods.
● Prevent deletion of a property if its state is not ‘New’ or ‘Canceled’
Tip: create a new method with the ondelete() decorator and remember that self can
be a recordset with more than one record.
● At offer creation, set the property state to ‘Offer Received’. Also raise an error if
the user tries to create an offer with a lower amount than an existing offer.
Tip: the property_id field is available in the vals, but it is an int. To instantiate an
estate.property object, use self.env[model_name].browse(value) (example)
Model Inheritance
Reference: the documentation related to this topic can be found in Inheritance and
extension.
In our real estate module, we would like to display the list of properties linked to a
salesperson directly in the Settings / Users & Companies / Users form view. To do this,
we need to add a field to the res.users model and adapt its view to show it.
The first inheritance mechanism allows modules to modify the behavior of a model
defined in an another module by:
class InheritedModel(models.Model):
_inherit = "inherited.model"
Task 40
Add a field to Users.
● Add the following field to res.users:
Field Type
In the next section let’s add the field to the view and check that everything is working
well!
View Inheritance
Reference: the documentation related to this topic can be found in Inheritance.
Note
Goal: at the end of this section, the list of available properties linked to a salesperson
should be displayed in their user form view
Instead of modifying existing views in place (by overwriting them), Odoo provides view
inheritance where children ‘extension’ views are applied on top of root views. These
extension can both add and remove content from their parent view.
An extension view references its parent using the inherit_id field. Instead of a single
view, its arch field contains a number of xpath elements that select and alter the
content of their parent view:
An XPath expression selecting a single element in the parent view. Raises an error if it
matches no element or more than one
position
Operation to apply to the matched element:
inside
appends xpath’s body to the end of the matched element
replace
replaces the matched element with the xpath’s body, replacing any $0 node occurrence
in the new body with the original element
before
inserts the xpath’s body as a sibling before the matched element
after
inserts the xpaths’s body as a sibling after the matched element
attributes
alters the attributes of the matched element using the special attribute elements in
the xpath’s body
When matching a single element, the position attribute can be set directly on the
element to be found. Both inheritances below have the same result.
Task 41
Add fields to the Users view.
Add the property_ids field to the base.view_users_form in a new notebook page.
Tip: an example an inheritance of the users’ view can be found here
Interact With Other Modules
In the previous section, we used inheritance to modify the behavior of a module. In our
real estate scenario, we would like to go a step further and be able to generate invoices
for our customers. Odoo provides an Invoicing module, so it would be neat to create an
invoice directly from our real estate module, i.e. once a property is set to ‘Sold’, an
invoice is created in the Invoicing application.
Any time we interact with another module, we need to keep in mind the modularity. If we
intend to sell our application to real estate agencies, some may want the invoicing
feature but others may not want it.
Link Module
The common approach for such use cases is to create a ‘link’ module. In our case, the
module would depend on estate and account and would include the invoice creation
logic of the estate property. This way the real estate and the accounting modules can be
installed independently. When both are installed, the link module provides the new
feature.
Task 42
Create a link module.
Create the estate_account module, which depends on the estate and account
modules. For now, it will be an empty shell.
Tip: you already did this at the beginning of the tutorial. The process is very similar.
When the estate_account module appears in the list, go ahead and install it! You’ll
notice that the Invoicing application is installed as well. This is expected since your
module depends on it. If you uninstall the Invoicing application, your module will be
uninstalled as well.
Invoice Creation
It’s now time to generate the invoice. We want to add functionality to the
estate.property model, i.e. we want to add some extra logic for when a property is
sold. Does that sound familiar? If not, it’s a good idea to go back to the previous chapter
since you might have missed something ;-)
As a first step, we need to extend the action called when pressing the ‘Sold’ button on a
property. To do so, we need to create a model inheritance in the estate_account
module for the estate.property model. For now, the overridden action will simply
return the super call. Maybe an example will make things clearer:
class InheritedModel(models.Model):
_inherit = "inherited.model"
def inherited_action(self):
return super().inherited_action()
Task 43
Add the first step of invoice creation.
● Create a estate_property.py file in the correct folder of the estate_account
module.
● _inherit the estate.property model.
● Override the action_sold method (you might have named it differently) to return
the super call.
Tip: to make sure it works, add a print or a debugger breakpoint in the overridden
method.
Is it working? If not, maybe check that all Python files are correctly imported.
If the override is working, we can move forward and create the invoice. Unfortunately,
there is no easy way to know how to create any given object in Odoo. Most of the time, it
is necessary to have a look at its model to find the required fields and provide
appropriate values.
A good way to learn is to look at how other modules already do what you want to do. For
example, one of the basic flows of Sales is the creation of an invoice from a sales order.
This looks like a good starting point since it does exactly what we want to do. Take
some time to read and understand the _create_invoices method. When you are done
crying because this simple task looks awfully complex, we can move forward in the
tutorial.
Task 44
Add the second step of invoice creation.
Create an empty account.move in the override of the action_sold method:
● the partner_id is taken from the current estate.property
● the move_type should correspond to a ‘Customer Invoice’
Tips:
● to create an object, use self.env[model_name].create(values), where values
is a dict.
● the create method doesn’t accept recordsets as field values.
When a property is set to ‘Sold’, you should now have a new customer invoice created in
Invoicing / Customers / Invoices.
Obviously we don’t have any invoice lines so far. To create an invoice line, we need the
following information:
Moreover, an invoice line needs to be linked to an invoice. The easiest and most efficient
way to link a line to an invoice is to include all lines at invoice creation. To do this, the
invoice_line_ids field is included in the account.move creation, which is a One2many.
One2many and Many2many use special ‘commands’ which have been made human
readable with the Command namespace. This namespace represents a triplet command
to execute on a set of records. The triplet was originally the only option to do these
commands, but it is now standard to use the namespace instead. The format is to place
them in a list which is executed sequentially. Here is a simple example to include a
One2many field line_ids at creation of a test.model:
def inherited_action(self):
self.env["test.model"].create(
{
"name": "Test",
"line_ids": [
Command.create({
"field_1": "value_1",
"field_2": "value_2",
})
],
}
)
return super().inherited_action()
Task 45
Add the third step of invoice creation.
Add two invoice lines during the creation of the account.move. Each property sold will
be invoiced following these conditions:
● 6% of the selling price
● an additional 100.00 from administrative fees
Tip: Add the invoice_line_ids at creation following the example above. For each line,
we need a name, quantity and price_unit.
You might already be familiar with existing engines such as Jinja (Python), ERB (Ruby)
or Twig (PHP). Odoo comes with its own built-in engine: QWeb Templates. QWeb is the
primary templating engine used by Odoo. It is an XML templating engine and used
mostly to generate HTML fragments and pages.
<kanban>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_global_click">
<field name="name"/>
</div>
</t>
</templates>
</kanban>
Let’s break down this example:
Once the Kanban view is working, we can start improving it. If we want to display an
element conditionally, we can use the t-if directive (see Conditionals).
<kanban>
<field name="state"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_global_click">
<field name="name"/>
</div>
<div t-if="record.state.raw_value == 'new'">
This is new!
</div>
</t>
</templates>
</kanban>
In the above example, the field name was added in the <templates> element, but state
is outside of it. When we need the value of a field but don’t want to display it in the view,
it is possible to add it outside of the <templates> element.
Task 47
Improve the Kanban view.
Add the following fields to the Kanban view: expected price, best price, selling price and
tags. Pay attention: the best price is only displayed when an offer is received, while the
selling price is only displayed when an offer is accepted.
Refer to the Goal of the section for a visual example.
Let’s give the final touch to our view: the properties must be grouped by type by default.
You might want to have a look at the various options described in Kanban.
Task 48
Add default grouping.
Use the appropriate attribute to group the properties by type by default. You must also
prevent drag and drop.
Refer to the Goal of the section for a visual example.
Coding guidelines
We will start refactoring the code to match to the Odoo coding guidelines. The
guidelines aim to improve the quality of the Odoo Apps code.
Reference: you will find the Odoo coding guidelines in Coding guidelines.
Task 49
Polish your code.
Refactor your code to respect the coding guidelines. Don’t forget to run your linter and
respect the module structure, the variable names, the method name convention, the
model attribute order and the xml ids.
Test on the runbot
Odoo has its own CI server named runbot. All commits, branches and PR will be tested
to avoid regressions or breaking of the stable versions. All the runs that pass the tests
are deployed on their own server with demo data.
Task 50
Play with the runbot.
Feel free to go to the runbot website and open the last stable version of Odoo to check
out all the available applications and functionalities.
File Structure
The bulk of a PDF report is its QWeb template. It also typically needs a corresponding
ir.actions.report to include the report within a module’s business logic. There is no
strict rule for the file names or where they are located, but these two parts are typically
stored in 2 separate files within a report folder at the top level of your module’s
directory. If a module has many or multiple long report templates, then they are often
organized logically across different files named after the report(s) they contain. All
actions for the reports are usually stored in the same file ending with _reports.xml,
regardless of the number of reports it contains.
Therefore, it is expected that your work tree will look something like this:
estate
├── models
│ ├── *.py
│ └── __init__.py
├── report
│ ├── estate_property_templates.xml
│ └── estate_property_reports.xml
├── security
│ └── ir.model.access.csv
├── views
│ └── *.xml
├── __init__.py
└── __manifest__.py
Don’t forget to add whatever files your template and action view will be into to your
__manifest__.py. In this case, you will want to add the files to the data list and
remember that the files listed in a manifest are loaded sequentially!
Basic Report
Note
Goal: at the end of this section, we will can print a report that displays all offers for a
property.
In our real estate example there are many useful reports that we could create. One
simple report we can create is one that displays all of a property’s offers.
Report Data
Before we do anything we first need some data to populate our reports or else this
tutorial won’t be very interesting. When creating reports, you will need some data to test
your report code and check that the resulting look is as expected. It is a good idea to
test with data that will cover most or all of your expected use cases. A good
representation set for our simple report is:
If you don’t have a set of data like this already, you can either:
● Complete the Define module data tutorial (if you haven’t done so already) and
add the extra cases to your demo data (you may need to create a new database
to load in the demo data).
● Manually create the data in your database.
● Copy this data file into a new directory (data) in your estate module and copy
these lines into your __manifest__.py file (you may need to create a new database
to load in the demo data).
Before continuing, click through your data in your database and make sure your data is
as expected. Of course you can add the data after you write your report code, but then
you will not be able to incrementally test portions of your code as you write it. This can
make checking for mistakes and debugging your code more difficult in the long run for
complicated reports.
Minimal Template
A minimal viable template is viewable under the “Minimal viable template” section of the
Report template documentation. We can modify this example to build our minimal
property offers template file:
● The use of the class="table" attribute, so our table has some nice formatting.
Twitter Bootstrap (we’re using its table class in this case), and Font Awesome
(useful for adding icons) classes can be used in your report template.
● The use of t-set, t-value, t-foreach, and t-as so that we can loop over all the
offer_ids.
If you are already familiar with website templating engines, then the QWeb directives
(i.e. the t- commands) probably don’t need much explanation and you can just look at
its documentation and skip ahead to the next subsection.
Otherwise you are encouraged to read more about them ( Wikipedia has a good high
level description), but the general idea is that QWeb provides the ability to dynamically
generate web code based on Odoo data and simple commands. I.e. QWeb can access
recordset data (and methods) and process simple programming operations such as
setting and accessing temporary variables. For example, in the above example:
● t-set creates a temporary variable called “offers” that has its value set by
t-value to the current estate.property recordset’s offer_ids.
● The t-foreach and t-as usage is the equivalent to the Python:
An ir.actions.report is primarily used via the Print menu of a model’s view. In the
practical example, the binding_model_id specifies which model’s views the report
should show, and Odoo will auto-matically add it for you. Another common use case of
the report action is to link it to a button as we learned in Chapter 10: Ready For Some
Action?. This is handy for reports that only make sense under specific conditions. For
example, if we wanted to make a “Final Sale” report, then we can link it to a “Print Sale
Info” button that appears in the form view only when the property is “Sold”.
You may have noticed or are wondered why our report template loops through a
recordset. When our template is passed more than one record, it can produce one PDF
report for all the records. Using the Print menu in the list view with multiple records
selected will demonstrate this.
Make a Report
Finally, you now know where to create your files and how the content of the files should
look. Happy report making!
Task
Make a report.
● Add the property offers report from the minimal template subsection to the Print
menu of the Property views.
● Improve the report by adding more data. Refer to the Goal of this section to see
what additional data you can add and feel free to add even more.
● Bonus: Make an extra flexible report by adding in some logic so that when there
are no offers on a property then we don’t create a table and instead write
something about how there are no offers yet. Hint: you will need to use t-if and
t-else.
Remember to check that your PDF reports match your data as expected.
Sub-templates
Note
Goal: at the end of this section, we will have a sub-template that we use in 2 reports.
There are two main reasons for using sub-templates. One is to make the code easier to
read when working with extra-long or complicated templates. The other is to reuse code
where possible. Our simple property offers report is useful, but listing property offers
information can be useful for more than just one report template. One example is a
report that lists all of a salesman’s properties’ offers.
See if you can understand how to call a sub-template by reading the documentation on
it and/or by looking at an example (remember QWeb uses the same control flows
regardless if it is for a report or a view in Odoo.)
Task
Create and use a sub-template.
● Split the table portion of the offers into its own template. Remember to check
that your original report still prints correctly afterwards.
● Add a new report for res.users that allows you to print all of the Real Estate
Properties that are visible in their form view (i.e. in the “Settings” app). Include
the offers for each of those saleman’s properties in the same report. Hint: since
the binding_model_id in this case will not be within the estate module, you will
need to use ref="base.model_res_users".
Your end result should look similar to the image in the Goal of this section.
Remember to check that your reports match your data as expected!
Report Inheritance
Note
Goal: at the end of this section, we will inherit the property report in the estate_account
module.
Inheritance in QWeb uses the same xpath elements as views inheritance. A QWeb
template refers to its parent template in a different way though. It is even easier to do by
just adding the inherit_id attribute to the template element and setting it equal to the
module.parent_template_id.
We didn’t add any new fields to any of the estate models in estate_account, but we can
still add information to our existing property report. For example, we know that any
“Sold” properties will already have an invoice created for them, so we can add this
information to our report.
Task
Inherit a report.
● Extend the property report to include some information about the invoice. You
can look at the Goal of this section for inspiration (i.e. print a line when the
property is Done, otherwise print nothing).
Again, remember to check that your reports match your data as expected!
Additional Features
All the following extra features are described further in the QWeb Reports
documentation, including how to implement each of them.
Translations
We all know Odoo is used in multiple languages thanks to automated and manual
translating. QWeb reports are no exception! Note that sometimes the translations do
not work properly if there are unnecessary spaces in your template’s text content, so try
to avoid them when possible (especially leading spaces).
Barcodes
Odoo has a built-in barcode image creator that allows for barcodes to be embedded in
your reports. Check out the corresponding code to see all the supported barcode types.
Grep
The grep command is a powerful tool used in Unix-based systems (such as Linux) for
searching and filtering text files. It allows you to find lines in a file that match a specific
pattern or regular expression.
Open the terminal window and use the grep command.
Remember, there must always be a location where you are searching for the text
Grep command:
1. grep -r ”seacrch_text” # this command will search the text in all
the files or directories present in current locations.
2. grep -r --include=’*.xml’ ‘seacrch_text’ # this command will search the text in
only xml files present in current locations.
3. grep -r --include=’*.py’ ‘seacrch_text’ # this command will search the text in
only python files present in current locations.
4. grep -r --include=’*.js’ ‘seacrch_text’ # this command will search the text in
only .js files present in current locations.
Runbot
In the context of software development and continuous integration (CI), a "runbot"
typically refers to a system or tool that automates the process of building, testing, and
deploying software. It is commonly used in the development of web applications and
software projects with multiple contributors.
To open runbot, follow the given steps:
1. Click on the link below.
runbot.odoo.com
2. Now you will have the screen as mentioned below
3. Now you have various versions of odoo, to open odoo 16.0 click on build
option>> connect to base.
4. Now enter user name : admin
5. Enter password : admin
6. Now you can explore odoo or check its functionality
Github
This section will help you to deploy your project on to your git repository
GitHub is a web-based platform that serves as a code hosting and version control repository for
Git, a distributed version control system. It provides a collaborative environment for developers
to work on projects, manage source code, track changes, and coordinate with team members.
2. Git: GitHub uses Git as its underlying version control system. Git allows multiple developers
to work on the same project simultaneously and tracks all changes made to the codebase over
time. It provides features like branching and merging, making it easier to manage and
collaborate on code.
3. Branches: A branch is a separate line of development that allows you to work on new
features or make changes to the codebase without affecting the main code. You can create
branches to experiment, develop new features, or fix bugs. Once the changes are complete,
you can merge the branch back into the main codebase.
4. Commits: A commit represents a snapshot of the codebase at a specific point in time. When
you make changes to the code, you create a commit to record those changes. Each commit has
a unique identifier and includes a message describing the changes made.
5. Pull Requests: A pull request is a way to propose changes to a repository. It allows you to
submit your changes and request a review from other team members. Pull requests provide a
platform for collaboration and discussion before merging the changes into the main codebase.
Git clone
git clone <paste the git path>
The `git clone` command is used to create a local copy of a remote repository. It allows you to
download all the files, commit history, and branches from a remote repository onto your local
machine. Here's a brief description of the `git clone` command:
The `git clone` command is followed by the URL of the remote repository you want to clone.
This URL can be obtained from the repository's webpage on a hosting service like GitHub.
ex-
```
git clone https://github.com/your-username/my-project.git
```
Once you execute the `git clone` command, Git creates a new directory on your local machine
with the same name as the repository. Inside this directory, it copies all the files and folders from
the remote repository.
By default, `git clone` creates a remote tracking branch named "origin/master," which
corresponds to the main branch of the remote repository. You can switch to this branch using
`git checkout master`.
Git checkout
git checkout<branch_name>
As we know whenever we clone any repository by default it creates a origin/main branch.
To change our branch we use ` git checkout` command
Git pull
git pull origin <branch_name>
The `git pull` command is used to update your local repository with the latest changes from the
remote repository. It combines two actions: fetching the latest commits from the remote
repository and automatically merging them into your current branch. This command is useful
when you want to incorporate changes made by others into your local codebase and keep it up
to date.
Git add
git add .
The `git add .` command is used to stage all modified and new files in the current directory and
its subdirectories for the next commit. It tells Git to start tracking these files and include them in
the staging area. This command is convenient when you have made changes to multiple files
and want to add them all at once. However, it does not include deleted files. To stage deleted
files as well, you can use `git add --all`. Once the files are staged, you can commit them using
`git commit` to permanently save the changes to the repository's history.
Git commit
git commit -m “<description about your module>”
Creating a Commit: Once you have staged the changes using git add command, you can
execute git commit followed by the -m option and a commit message enclosed in quotes. The
commit message should be concise and describe the purpose or nature of the changes made.
Commit History: Commits are stored chronologically, forming a commit history. You can view the
commit history using commands like git log to see details of each commit, such as the commit
message, author, and date.
Git push
git push origin <branch_name>
The git push command is used to upload your local repository's commits and changes to a
remote repository. It allows you to share your work with others and update the remote repository
with your latest changes.
Database
Database creation
Command to create database
Click on restore database, then you will have a screen as shown below, fill the master password
and database name and click on continue
Duplicate Database
● As you can see the delete option also present at /web/database/manager
● Click on Duplicate, a dialog box will appear as mentioned in below image
● Type master password, new database name then click on Continue
Copy the database content