The view layer describes the user interface. Views are defined using XML, which is used by the web client framework to generate data-aware HTML views.
We have menu items that can activate actions that can render views. For example, the Users menu item processes an action also called Users, that in turn renders a series of views. There are several view types available, such as the list and form views, and the filter options made available are also defined by a particular type of view, the search view.
The Odoo development guidelines states that the XML files defining the user interface should be placed inside a views/
subdirectory.
Let's start creating the user interface for our To-Do application.
Now that we have a model to store our data, we should make it available on the user interface.
For that, we should add a menu option to open the To-do Task
model so that it can be used.
Create the views/todo_menu.xml
file to define a menu item and the action performed by it:
<?xml version="1.0"?> <odoo> <!-- Action to open To-do Task list --> <act_window id="action_todo_task" name="To-do Task" res_model="todo.task" view_mode="tree,form" /> <!-- Menu item to open To-do Task list --> <menuitem id="menu_todo_task" name="Todos" action="action_todo_task" /> </odoo>
The user interface, including menu options and actions, is stored in database tables. The XML file is a data file used to load those definitions into the database when the module is installed or upgraded. The preceding code is an Odoo data file, describing two records to add to Odoo:
<act_window>
element defines a client-side window action that will open the todo.task
model with the tree
and form
views enabled, in that order.<menuitem>
defines a top menu item calling the action_todo_task
action, which was defined before.Both elements include an id
attribute. This id
attribute also called an XML ID, is very important: it is used to uniquely identify each data element inside the module, and can be used by other elements to reference it. In this case, the <menuitem>
element needs to reference the action to process, and needs to make use of the <act_window>
ID for that. XML IDs are discussed in greater detail in Chapter 4, Module Data.
Our module does not yet know about the new XML data file. This is done by adding it to the data
attribute in the __manifest__.py
file. It holds the list of files to be loaded by the module. Add this attribute to the manifest's dictionary:
'data': ['views/todo_menu.xml'],
Now we need to upgrade the module again for these changes to take effect. Go to the Todos top menu and you should see our new menu option available:
Even though we haven't defined our user interface view, clicking on the Todos menu will open an automatically generated form for our model, allowing us to add and edit records.
Odoo is nice enough to automatically generate them so that we can start working with our model right away.
So far, so good! Let's improve our user interface now. Try making gradual improvements as shown in the next sections, doing frequent module upgrades, and don't be afraid to experiment. You might also want to try the --dev=all
server option. Using it the view definitions are read directly from the XML files so that your changes can be immediately available to Odoo without the need of a module upgrade.
If an upgrade fails because of an XML error, don't panic! Comment out the last edited XML portions or remove the XML file from __manifest__.py
and repeat the upgrade. The server should start correctly. Now read the error message in the server log carefully: it should point you to where the problem is.
Odoo supports several types of views, but the three most important ones are: tree
(usually called list views), form
, and search
views. We'll add an example of each to our module.
All views are stored in the database, in the ir.ui.view
model. To add a view to a module, we declare a <record>
element describing the view in an XML file, which is to be loaded into the database when the module is installed.
Add this new views/todo_view.xml
file to define our form view:
<?xml version="1.0"?> <odoo> <record id="view_form_todo_task" model="ir.ui.view"> <field name="name">To-do Task Form</field> <field name="model">todo.task</field> <field name="arch" type="xml"> <form string="To-do Task"> <group> <field name="name"/> <field name="is_done"/> <field name="active" readonly="1"/> </group> </form> </field> </record> </odoo>
Remember to add this new file to the data
key in the manifest file, otherwise, our module won't know about it and it won't be loaded.
This will add a record to the ir.ui.view
model with the identifier view_form_todo_task
. The view is for the todo.task
model and is named To-do Task Form
. The name is just for information; it does not have to be unique, but it should allow one to easily identify which record it refers to. In fact, the name can be entirely omitted, in that case, it will be automatically generated from the model name and the view type.
The most important attribute is arch
, and it contains the view definition, highlighted in the XML code above. The <form>
tag defines the view type, and in this case, contains three fields. We also added an attribute to the active
field to make it read only.
The preceding section provided a basic form view, but we can make some improvements on it. For document models, Odoo has a presentation style that mimics a paper page. This form contains two elements: <header>
to contain action buttons and <sheet>
to contain the data fields.
We can now replace the basic <form>
defined in the previous section with this one:
<form> <header> <!-- Buttons go here--> </header> <sheet> <!-- Content goes here: --> <group> <field name="name"/> <field name="is_done"/> <field name="active" readonly="1"/> </group> </sheet> </form>
Forms can have buttons to perform actions. These buttons are able to run window actions such as opening another form or run Python functions defined in the model.
They can be placed anywhere inside a form, but for document-style forms, the recommended place for them is the <header>
section.
For our application, we will add two buttons to run the methods of the todo.task
model:
<header>
<button name="do_toggle_done" type="object"
string="Toggle Done" class="oe_highlight" />
<button name="do_clear_done" type="object"
string="Clear All Done" />
</header>
The basic attributes of a button comprise the following:
string
with the text to display on the buttontype
of action it performsname
is the identifier for that actionclass
is an optional attribute to apply CSS styles, like in regular HTMLThe <group>
tag allows you to organize the form content. Placing <group>
elements inside a <group>
element creates a two column layout inside the outer group. Group elements are advised to have a name
attribute so that its easier for other modules to extend them.
We will use this to better organize our content. Let's change the <sheet>
content of our form to match this:
<sheet> <group name="group_top"> <group name="group_left"> <field name="name"/> </group> <group name="group_right"> <field name="is_done"/> <field name="active" readonly="1"/> </group> </group> </sheet>
At this point, our todo.task
form view should look like this:
<form> <header> <button name="do_toggle_done" type="object" string="Toggle Done" class="oe_highlight" /> <button name="do_clear_done" type="object" string="Clear All Done" /> </header> <sheet> <group name="group_top"> <group name="group_left"> <field name="name"/> </group> <group name="group_right"> <field name="is_done"/> <field name="active" readonly="1" /> </group> </group> </sheet> </form>
The action buttons won't work yet since we still need to add their business logic.
When viewing a model in list mode, a <tree>
view is used. Tree views are capable of displaying lines organized in hierarchies, but most of the time, they are used to display plain lists.
We can add the following tree
view definition to todo_view.xml
:
<record id="view_tree_todo_task" model="ir.ui.view"> <field name="name">To-do Task Tree</field> <field name="model">todo.task</field> <field name="arch" type="xml"> <tree colors="decoration-muted:is_done==True"> <field name="name"/> <field name="is_done"/> </tree> </field> </record>
This defines a list with only two columns: name
and is_done
. We also added a nice touch: the lines for done tasks (is_done==True
) are shown grayed out. This is done applying the Bootstrap class muted
. Check http://getbootstrap.com/css/#helper-classes-colors for more information on Bootstrap and its contextual colors.
At the top-right corner of the list, Odoo displays a search box. The fields it searches in and the available filters are defined by a <search>
view.
As before, we will add this to todo_view.xml
:
<record id="view_filter_todo_task" model="ir.ui.view">
<field name="name">To-do Task Filter</field>
<field name="model">todo.task</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<filter string="Not Done"
domain="[('is_done','=',False)]"/>
<filter string="Done"
domain="[('is_done','!=',False)]"/>
</search>
</field>
</record>
The <field>
elements define fields that are also searched when typing in the search box. The <filter>
elements add predefined filter conditions, that can be toggled with a user click, defined using a specific syntax.