14 min read

Odoo uses a client/server architecture in which clients are web browsers accessing the Odoo server via RPC. Both the server and client extensions are packaged as modules which are optionally loaded in a database.

Odoo modules can either add brand new business logic to an Odoo system or alter and extend existing business logic. Everything in Odoo starts and ends with modules.

In this article, we will cover the basics of creating Odoo Addon Modules. Recipes that we will cover in this.

  • Creating and installing a new addon module
  • Completing the addon module manifest
  • Organizing the addon module file structure
  • Adding Models

Our main goal here is to understand how an addon module is structured and the typical incremental workflow to add components to it.

This post is an excerpt from the book Odoo 11 Development Cookbook – Second Edition by Alexandre Fayolle and Holger Brunn. With this book, you can create fast and efficient server-side applications using the latest features of Odoo v11.

For this article, you should have Odoo installed. You are also expected to be comfortable in discovering and installing extra addon modules.

Creating and installing a new addon module

In this recipe, we will create a new module, make it available in our Odoo instance, and install it.

Getting ready

We will need an Odoo instance ready to use.

For explanation purposes, we will assume Odoo is available at ~/odoo-dev/odoo, although any other location of your preference can be used.

We will also need a location for our Odoo modules. For the purpose of this recipe, we will use a local-addons directory alongside the odoo directory, at
~/odoo-dev/local-addons.

How to do it…

The following steps will create and install a new addon module:

  1. Change the working directory in which we will work and create the addons directory where our custom module will be placed:
$ cd ~/odoo-dev
$ mkdir local-addons
  1. Choose a technical name for the new module and create a directory with that name for the module. For our example, we will use my_module:
$ mkdir local-addons/my_module
A module’s technical name must be a valid Python identifier; it must begin with a letter, and only contain letters (preferably lowercase), numbers, and underscore characters.
  1. Make the Python module importable by adding an __init__.py file:
$ touch local-addons/my_module/__init__.py
  1. Add a minimal module manifest for Odoo to detect it. Create a __manifest__.py file with this line:
{'name': 'My module'}
  1. Start your Odoo instance including our module directory in the addons path:
$ odoo/odoo-bin --addons-path=odoo/addon/,local-addons/
If the --save option is added to the Odoo command, the addons path will be saved in the configuration file. Next time you start the server, if no addons path option is provided, this will be used.
  1. Make the new module available in your Odoo instance; log in to Odoo using admin, enable the Developer Mode in the About box, and in the Apps top menu, select Update Apps List. Now, Odoo should know about our Odoo module.
  2. Select the Apps menu at the top and, in the search bar in the top-right, delete the default Apps filter and search for my_module. Click on its Install button, and the installation will be concluded.

How it works…

An Odoo module is a directory containing code files and other assets. The directory name used is the module’s technical name. The name key in the module manifest is its title.

The __manifest__.py file is the module manifest. It contains a Python dictionary with information about the module, the modules it depends on, and the data files that it will load.

In the example, a minimal manifest file was used, but in real modules, we will want to add a few other important keys. These are discussed in the Completing the module manifest recipe, which we will see next.

The module directory must be Python-importable, so it also needs to have an __init__.py file, even if it’s empty. To load a module, the Odoo server will import it. This will cause the code in the __init__.py file to be executed, so it works as an entry point to run the module Python code. Due to this, it will usually contain import statements to load the module Python files and submodules.

Known modules can be installed directly from the command line using the --init or
-i option. This list is initially set when you create a new database, from the modules found on the addons path provided at that time. It can be updated in an existing database with the Update Module List menu.

Completing the addon module manifest

The manifest is an important piece for Odoo modules. It contains important information about the module and declares the data files that should be loaded.

Getting ready

We should have a module to work with, already containing a __manifest__.py manifest file. You may want to follow the previous recipe to provide such a module to work with.

How to do it…

We will add a manifest file and an icon to our addon module:

  1. To create a manifest file with the most relevant keys, edit the module __manifest__.py file to look like this:
{ 
    'name': "Title", 
    'summary': "Short subtitle phrase", 
    'description': """Long description""", 
    'author': "Your name", 
    'license': "AGPL-3", 
    'website': "http://www.example.com", 
    'category': 'Uncategorized', 
    'version': '11.0.1.0.0', 
    'depends': ['base'], 
    'data': ['views.xml'], 
    'demo': ['demo.xml'], 
}
  1. To add an icon for the module, choose a PNG image to use and copy it to
    static/description/icon.png.

How it works…

The remaining content is a regular Python dictionary, with keys and values. The example manifest we used contains the most relevant keys:

  • name: This is the title for the module.
  • summary: This is the subtitle with a one-line description.
  • description: This is a long description written in plain text or the ReStructuredText (RST) format. It is usually surrounded by triple quotes, and is used in Python to delimit multiline texts. For an RST quickstart reference, visit http://docutils.sourceforge.net/docs/user/rst/quickstart.html.
  • author: This is a string with the name of the authors. When there is more than one, it is common practice to use a comma to separate their names, but note that it still should be a string, not a Python list.
  • license: This is the identifier for the license under which the module is made available. It is limited to a predefined list, and the most frequent option is AGPL-3. Other possibilities include LGPL-3, Other OSI approved license, and Other proprietary.
  • website: This is a URL people should visit to know more about the module or the authors.
  • category: This is used to organize modules in areas of interest. The list of the standard category names available can be seen at https://github.com/odoo/odoo/blob/11.0/odoo/addons/base/module/module_data.xml. However, it’s also possible to define other new category names here.
  • version: This is the modules’ version numbers. It can be used by the Odoo app store to detect newer versions for installed modules. If the version number does not begin with the Odoo target version (for example, 11.0), it will be automatically added. Nevertheless, it will be more informative if you explicitly state the Odoo target version, for example, using 11.0.1.0.0 or 11.0.1.0 instead of 1.0.0 or 1.0.
  • depends: This is a list with the technical names of the modules it directly depends on. If none, we should at least depend on the base module. Don’t forget to include any module defining XML IDs, Views, or Models referenced by this module. That will ensure that they all load in the correct order, avoiding hard-to-debug errors.
  • data: This is a list of relative paths to the data files to load with module installation or upgrade. The paths are relative to the module root directory. Usually, these are XML and CSV files, but it’s also possible to have YAML data files.
  • demo: This is the list of relative paths to the files with demonstration data to load. These will only be loaded if the database was created with the Demo Data flag enabled.

The image that is used as the module icon is the PNG file at static/description/icon.png.

Odoo is expected to have significant changes between major versions, so modules built for one major version are likely to not be compatible with the next version without conversion and migration work. Due to this, it’s important to be sure about a module’s Odoo target version before installing it.

There’s more…

Instead of having the long description in the module manifest, it’s possible to have it in its own file. Since version 8.0, it can be replaced by a README file, with either a .txt, .rst, or an .md (Markdown) extension. Otherwise, include a description/index.html file in the module.

This HTML description will override a description defined in the manifest file.

There are a few more keys that are frequently used:

  • application: If this is True, the module is listed as an application. Usually, this is used for the central module of a functional area
  • auto_install: If this is True, it indicates that this is a “glue” module, which is automatically installed when all of its dependencies are installed
  • installable: If this is True (the default value), it indicates that the module is available for installation

Organizing the addon module file structure

An addon module contains code files and other assets such as XML files and images. For most of these files, we are free to choose where to place them inside the module directory.

However, Odoo uses some conventions on the module structure, so it is advisable to follow them.

Getting ready

We are expected to have an addon module directory with only the __init__.py and __manifest__.py files. In this recipe, we suppose this is local-addons/my_module.

How to do it…

To create the basic skeleton for the addon module, perform the given steps:

  1. Create the directories for code files:
$ cd local-addons/my_module
$ mkdir models
$ touch models/__init__.py
$ mkdir controllers
$ touch controllers/__init__.py
$ mkdir views
$ mkdir security
$ mkdir data
$ mkdir demo
$ mkdir i18n
$ mkdir -p static/description
  1. Edit the module’s top __init__.py file so that the code in subdirectories is loaded:
from . import models
from . import controllers

This should get us started with a structure containing the most used directories, similar to this one:

.
├── __init__.py
├── __manifest__.py

├── controllers
│   └── __init__.py
├── data
├── demo
├── i18n 
├── models 
│ └── __init__.py 
├── security 
├── static 
│ └── description 
└── views

How it works…

To provide some context, an Odoo addon module can have three types of files:

  •  The Python code is loaded by the __init__.py files, where the .py files and code subdirectories are imported. Subdirectories containing code Python, in turn, need their own __init__.py.
  • Data files that are to be declared in the data and demo keys of the __manifest__.py module manifest in order to be loaded are usually XML and CSV files for the user interface, fixture data, and demonstration data. There can also be YAML files, which can include some procedural instructions that are run when the module is loaded, for instance, to generate or update records programmatically rather than statically in an XML file.
  • Web assets such as JavaScript code and libraries, CSS, and QWeb/HTML templates also play an important part. There are declared through an XML file extending the master templates to add these assets to the web client or website pages.

The addon files are to be organized in these directories:

  • models/ contains the backend code files, creating the Models and their business logic. A file per Model is recommended, with the same name as the model, for example, library_book.py for the library.book model.
  • views/ contains the XML files for the user interface, with the actions, forms, lists, and so on. As with models, it is advised to have one file per model. Filenames for website templates are expected to end with the _template suffix.
  • data/ contains other data files with module initial data.
  • demo/ contains data files with demonstration data, useful for tests, training, or module evaluation.
  • i18n/ is where Odoo will look for the translation .pot and .po files. These files don’t need to be mentioned in the manifest file.
  • security/ contains the data files defining access control lists, usually a ir.model.access.csv file, and possibly an XML file to define access Groups and Record Rules for row level security.
  • controllers/ contains the code files for the website controllers, and for modules providing that kind of feature.
  • static/ is where all web assets are expected to be placed. Unlike other directories, this directory name is not just a convention, and only files inside it can be made available for the Odoo web pages. They don’t need to be mentioned in the module manifest, but will have to be referred to in the web template.
When adding new files to a module, don’t forget to declare them either in the __manifest__.py (for data files) or __init__.py (for code files); otherwise, those files will be ignored and won’t be loaded.

Adding models

Models define the data structures to be used by our business applications. This recipe shows how to add a basic model to a module.

We will use a simple book library example to explain this; we want a model to represent books. Each book has a name and a list of authors.

Getting ready

We should have a module to work with. We will use an empty my_module for our explanation.

How to do it…

To add a new Model, we add a Python file describing it and then upgrade the addon module (or install it, if it was not already done). The paths used are relative to our addon module location (for example, ~/odoo-dev/local-addons/my_module/):

  1. Add a Python file to the models/library_book.py module with the following code:
from odoo import models, fields     
class LibraryBook(models.Model): 
    _name = 'library.book' 
    name = fields.Char('Title', required=True) 
    date_release = fields.Date('Release Date')
    author_ids = fields.Many2many(
        'res.partner', 
        string='Authors'
    )
  1. Add a Python initialization file with code files to be loaded by the models/__init__.py module with the following code:
from . import library_book
  1. Edit the module Python initialization file to have the models/ directory loaded by the module:
from . import models
  1. Upgrade the Odoo module, either from the command line or from the apps menu in the user interface. If you look closely at the server log while upgrading the module, you should see this line:
odoo.modules.registry: module my_module: creating or updating database table

After this, the new library.book model should be available in our Odoo instance. If we have the technical tools activated, we can confirm that by looking it up at Settings | Technical | Database Structure | Models.

How it works…

Our first step was to create a Python file where our new module was created.

Odoo models are objects derived from the Odoo Model Python class.

When a new model is defined, it is also added to a central model registry. This makes it easier for other modules to make modifications to it later.

Models have a few generic attributes prefixed with an underscore. The most important one is _name, providing a unique internal identifier to be used throughout the Odoo instance.

The model fields are defined as class attributes. We began defining the name field of the Char type. It is convenient for models to have this field, because by default, it is used as the record description when referenced from other models.

We also used an example of a relational field, author_ids. It defines a many-to-many relation between Library Books and the partners. A book can have many authors and each author can have written many books.

Next, we must make our module aware of this new Python file. This is done by the __init__.py files. Since we placed the code inside the models/ subdirectory, we need the previous __init__ file to import that directory, which should, in turn, contain another __init__ file importing each of the code files there (just one, in our case).

Changes to Odoo models are activated by upgrading the module. The Odoo server will handle the translation of the model class into database structure changes.

Although no example is provided here, business logic can also be added to these Python files, either by adding new methods to the Model’s class or by extending the existing methods, such as create() or write().

Thus we learned about the structure of an Odoo addon module and learned, step-by-step, how to create a simple module from scratch.

To know more about, how to define access rules for your data;  how to expose your data models to end users on the back end and on the front end; and how to create beautiful PDF versions of your data, read this book Odoo 11 Development Cookbook – Second Edition.

Read Next:

ERP tool in focus: Odoo 11

Building Your First Odoo Application

How to Scaffold a New module in Odoo 11


Subscribe to the weekly Packt Hub newsletter. We'll send you the results of our AI Now Survey, featuring data and insights from across the tech landscape.

* indicates required

LEAVE A REPLY

Please enter your comment!
Please enter your name here