Skip to main content

The view layer

The view layer

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, which in turn renders a series of views. There are several view types available, such as the list (sometimes called tree for historical reasons) and form views, and the filter options made available in the top-right search box are also defined by a particular type of view, the search view.
The Odoo development guidelines state 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.
In the next sections, we will make gradual improvements and frequent module upgrades to make those changes available. You might also want to try the --dev=all server option, which spares us from module upgrades while developing. Using it, the view definitions are read directly from the XML files so that your changes are immediately available to Odoo without the need for a module upgrade.

Note

If an upgrade fails because of an XML error, don't panic! Read the error message in the server log carefully; it should point you to where the problem is. If you feel in trouble, just comment out the last edited XML portions or remove the XML file from __manifest__.py and repeat the upgrade. The server should start correctly.

Adding menu items

Now that we have the places to store our data, we want to have it available on the user interface. The first thing to do is add the corresponding menu options.
Edit the views/library_menu.xml file and, inside the <odoo> XML element, add the records that define a menu item and the action performed by it:
  <!-- Action to open the Book list --> 
  <act_window id="action_library_book" 
    name="Library Books" 
    res_model="library.book" 
    view_mode="tree,form"
  /> 
  <!-- Menu item to open the Book list -->
  <menuitem id="menu_library_book" 
    name="Books" 
    parent="library_menu"
    action="action_library_book"
  /> 
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 addon module is installed or upgraded. The preceding code is an Odoo data file, describing two records to add to Odoo:
  • The <act_window> element defines a client-side window action that will open the library.book model with the tree and form views enabled, in that order
  • The <menuitem> defines a top menu item that calls the action_library_book action, which was defined before
We now need to upgrade the module again for these changes to take effect. Then, after a browser-page refresh, we should see the Library top menu, and it should have a submenu option available. Clicking on it displays a basic list view, and records can be edited using an automatically-generated form view. We can see it if we click on the Createbutton:
Even though we haven't defined our user interface view, the automatically-generated list and form views are functional, and allow us to start editing data right away.

Creating the form view

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/book_view.xml file to define the form view:
<?xml version="1.0"?> 
<odoo> 
  <record id="view_form_book" model="ir.ui.view"> 
    <field name="name">Book Form</field> 
    <field name="model">library.book</field> 
    <field name="arch" type="xml"> 
      <form string="Book">        <group>
          <field name="name" /> 
          <field name="author_ids" widget="many2many_tags" /> 
          <field name="publisher_id" /> 
          <field name="date_published" /> 
          <field name="isbn" /> 
          <field name="active" />           <field name="image" widget="image" /> 
        </group> 
      </form> 
    </field> 
  </record> 
</odoo> 
The ir.ui.view record has values for three fields: name, model, and arch. Another important element is the record id. It defines an XML ID identifier that can be used for other records to reference it.
The view is for the library.book model and is named Book Form. The name is just for information purpose; it does not have to be unique, but it should allow you 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 field is arch, as it contains the view definition, highlighted in the previously listed XML code. The <form> tag defines the view type, and it contains the view structure.
In this case, it is the <group> element that contains the fields to be shown in the form.The fields automatically use an appropriate default widget, such as the date-selection widget for date fields. In some cases, we may want to use a different widget. That is the case for author_ids, which is using a widget to display the authors as a list of tags, and the image field, which is using a widget appropriate for handling images. A detailed explanation of view elements is provided in Chapter 10, Backend Views – Design the User Interface.
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:
'data': [
    'security/library_security.xml',
    'security/ir.model.access.csv',
    'views/library_menu.xml',
'views/book_view.xml',
],
Remember that, for the changes to be loaded to our Odoo database, a module upgrade is needed. To see the changes in the web client, the form needs to be reloaded. Either click again on the menu option that opens it or reload the browser page (F5 in most browsers).

Business document form views

The preceding section provided a basic form view, but we can make some improvements to 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 data fields.
We can now replace the basic <form> defined in the previous section with this one:
<form> 
  <header> 
    <!-- Buttons will go here --> 
  </header> 
<sheet> 
    <!-- Content goes here: --> 
    <group>
      <field name="name" /> 
      <field name="author_ids" widget="many2many_tags" /> 
      <field name="publisher_id" /> 
      <field name="date_published" /> 
      <field name="isbn" /> 
      <field name="active" /> 
      <field name="image" widget="image" /> 
    </group> 
  </sheet> 
</form> 

Adding action buttons

Forms can have buttons to perform actions. These buttons are able to run window actions such as opening another form or running 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 a field for the book ISBN, and have a button to check whether the ISBN is valid. The code for this will be in a method of the Book model. We will name it button_check_isbn().


Even though we don't have the method created yet, we can already add the corresponding button to the form:
<header> 
  <button name="button_check_isbn" type="object" 
          string="Check ISBN" /> 
</header> 
The basic attributes of a button consist of the following:
  • The string with the text to display on the button
  • The type of action it performs
  • name is the identifier for that action
  • class is an optional attribute to apply CSS styles, as in regular HTML

Using groups to organize forms

The <group> tag allows you to organize form content. Placing <group> elements inside a <group> element creates a two-column layout inside the outer group. It is advisable for group elements to have a name attribute so that it's 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" />
      <field name="author_ids" widget="many2many_tags" />
      <field name="publisher_id" />
      <field name="date_published" />
</group> 
    <group name="group_right"> 
      <field name="isbn" /> 
      <field name="active" /> 
      <field name="image" widget="image" />
</group> 
  </group> 
</sheet>

The complete form view

At this point, our library.book form view should look like this:
<form> 
  <header> 
    <button name="check_isbn" type="object" 
      string="Check ISBN" /> 
  </header> 
  <sheet> 
    <group name="group_top"> 
      <group name="group_left"> 
        <field name="name" />
        <field name="author_ids" widget="many2many_tags" />
        <field name="publisher_id" />
        <field name="date_published" />
      </group> 
      <group name="group_right"> 
        <field name="isbn" /> 
        <field name="active" /> 
        <field name="image" widget="image" />
      </group> 
    </group> 
  </sheet> 
</form> 
The action buttons won't work yet, since we still need to add their business logic.

Adding list and search views

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 book_view.xml:
<record id="view_tree_book" model="ir.ui.view"> 
  <field name="name">Book List</field> 
  <field name="model">library.book</field> 
  <field name="arch" type="xml"> 
    <tree> 
      <field name="name"/> 
      <field name="author_ids" widget="many2many_tags" />
      <field name="publisher_id"/> 
      <field name="date_published"/>
    </tree> 
  </field> 
</record> 
This defines a list with four columns: name, author_ids, publisher_id, and date_published.
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 book_view.xml:
<record id="view_search_book" model="ir.ui.view"> 
  <field name="name">Book Filters</field> 
  <field name="model">library.book</field> 
  <field name="arch" type="xml"> 
    <search> 
      <field name="publisher_id"/> 
      <filter name="filter_inactive"
              string="Inactive" 
              domain="[('active','=',True)]"/>
      <filter name="filter_active"
              string="Active"
              domain="[('active','=',False)]"/> 
    </search> 
  </field> 
</record> 
The <field> elements define fields that are also searched when typing in the search box. We added publisher_id to automatically suggest searching in the publisher field. The <filter> elements add predefined filter conditions, which can be toggled with a user click, are defined using a specific syntax. This will be addressed in more detail in Backend Views – Design the User Interface.

Note

Changed in Odoo 12<filter> elements are now required to have a name="..." attribute, uniquely identifying each filter definition. If missing, the XML validation will fail and the module will not install or upgrade.

Comments

Popular posts from this blog

The message and activity features

The message and activity features Odoo has available global  messaging  and activity planning features, provided by the  Discuss  application, with technical name mail. The mail module provides the  mail.thread  abstract class that makes it simple to add the messaging features to any model, and the  mail.activity.mixin  that adds planned activity features. This was done in  Chapter 4 ,  Extending Modules , to explain how to inherit features from mixin abstract classes. To add these features, we need to add the mail dependency to the add-on module,  library_checkout , and then have the library checkout model class inherit from the abstract classes providing the following features. Edit the  'depends'  key in the  library_checkout/__manifest__.py   file, to add the mail module, shown as follows: Copy 'depends': ['library_member' , 'mail' ], And edit the  library_checkout/m...

Setting up an nginx reverse proxy

Setting up an nginx reverse proxy While Odoo itself can serve web pages, it's strongly recommended that there is a  reverse  proxy positioned in front of it. A reverse proxy acts as an intermediary that manages the traffic between clients sending requests and the Odoo servers responding to them. Using a reverse proxy has several benefits. On the security side, it can do the following: Handle (and enforce) HTTPS protocols to encrypt traffic Hide the internal network characteristics Act as an application firewall, limiting the URLs accepted for processing Also, on the performance side, it can provide the following significant improvements: Cached static content, hence reducing the load on the Odoo servers Compressed content to speed up loading time Act as a load balancer, distributing load between several servers Apache is a popular choice when considering a reverse proxy, although  nginx  is a recent alternative with good technical argumen...

The QWeb template language

The QWeb template language The  QWeb  parser looks for special directives in the templates and replaces them with dynamically generated HTML. These directives are XML element attributes and can be used in any valid tag or element, such as  <div> ,  <span> , or  <field> . Sometimes, we may want to use a QWeb directive but we don't want to place it in any of the XML elements in our template. For those cases, we have a  <t>  special element that can have QWeb directives, such as  t-if  or  t-foreach , but is silent and won't have any output on the final XML/HTML produced. The QWeb directives will frequently make use of evaluated expressions to produce different results, depending on the current record values. There are two different QWeb implementations: client-side JavaScript and server-side Python. The reports and website pages use the server-side Python implementation of QWeb. Kanban views us...