Odoo operates on a robust client/server architecture where web browsers serve as clients, interacting with the Odoo server via Remote Procedure Calls (RPC). Both server-side and client-side extensions are encapsulated within modules, which are optionally loaded into a database. These Odoo modules are fundamental, serving as the building blocks for introducing entirely new business logic or for modifying and extending existing functionalities within an Odoo system. Essentially, every operation and feature within Odoo is driven by its modular design.
This article aims to provide a fundamental understanding of how to create Odoo Addon Modules. We will explore key aspects including:
- Creating and installing a new addon module
- Completing the addon module manifest
- Organizing the addon module's file structure
- Adding Models
Our primary objective is to demystify the structure of an addon module and outline the typical incremental development workflow involved in adding new components to it.
To follow along with this article, it is assumed that you have an Odoo instance already installed and operational. Furthermore, a basic familiarity with discovering and installing additional addon modules will be beneficial.
Creating and Installing a New Addon Module
In this section, we will guide you through the process of creating a brand new module, making it accessible within your Odoo instance, and successfully installing it.
Prerequisites and Setup
Before we begin, ensure you have an Odoo instance readily available for use. For the purpose of this explanation, we will assume Odoo is located at ~/odoo-dev/odoo, though you are free to use any other preferred location on your system.
Additionally, you will need a designated location for your custom Odoo modules. In this guide, we will establish a local-addons directory positioned alongside your Odoo installation, specifically at ~/odoo-dev/local-addons. This will serve as the central repository for all your custom modules.
Step-by-Step Installation Process
Follow these steps to create and install your new addon module:
- Begin by navigating to your working directory and creating the necessary addons directory where your custom module will reside:
$ cd ~/odoo-dev $ mkdir local-addons - Next, choose a unique technical name for your new module and create a corresponding directory. For this example, we will use
my_module:$ mkdir local-addons/my_moduleIt is important that a module's technical name adheres to Python identifier rules: it must start with a letter and contain only letters (preferably lowercase), numbers, and underscore characters.
- To make your new module importable by Python, add an
__init__.pyfile within its directory:$ touch local-addons/my_module/__init__.py - For Odoo to recognize your module, you must add a minimal module manifest. Create a
__manifest__.pyfile containing the following line:{'name': 'My module'} - Now, start your Odoo instance, ensuring that our newly created module directory is included in the addons path:
$ odoo/odoo-bin --addons-path=odoo/addon/,local-addons/If you append the
--saveoption to the Odoo command, the specified addons path will be stored in the configuration file. This ensures that the next time you start the server without explicitly providing an addons path, this saved path will be utilized automatically. - To make the new module available within your Odoo instance, log in to Odoo as an administrator. Enable Developer Mode via the 'About' box, then navigate to the 'Apps' top menu and select 'Update Apps List'. This action will inform Odoo about your newly added module.
- Finally, access the 'Apps' menu at the top. In the search bar on the top-right, clear any default 'Apps' filter and search for
my_module. Click on its 'Install' button to complete the installation process.
Understanding the Installation Process
An Odoo module is essentially a directory that encapsulates various code files and other assets. The name given to this directory serves as the module's technical name, while the name key within the module manifest acts as its display title.
The __manifest__.py file is crucial; it serves as the module's manifest. This file is a Python dictionary containing vital information about the module, including its dependencies on other modules and a declaration of the data files it needs to load.
While our example used a minimal manifest file, real-world modules typically incorporate several other important keys, which will be discussed in detail in the subsequent section, "Completing the Addon Module Manifest."
For the module directory to be importable by Python, it must contain an __init__.py file, even if initially empty. When the Odoo server loads a module, it imports it, causing the code within the __init__.py file to execute. This file thus acts as an entry point for running the module's Python code and usually contains import statements to load the module's Python files and submodules.
Known modules can also be installed directly from the command line using the --init or -i option. The list of modules is initially set during database creation, based on the addons path provided at that time. For existing databases, this list can be updated using the 'Update Module List' menu option.
Completing the Addon Module Manifest
The manifest file is a cornerstone for Odoo modules, holding essential information about the module and declaring the data files that need to be loaded during its operation.
Prerequisites
You should have an existing module to work with, which already contains a __manifest__.py file. If you haven't created one yet, it is recommended to follow the steps outlined in the previous section to set up such a module.
Adding Manifest Details and Icon
We will now enhance our addon module by populating its manifest file with relevant keys and adding a descriptive icon:
- To create a comprehensive manifest file that includes the most relevant keys, modify your module's
__manifest__.pyfile to match the following structure:{ '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'], } - To assign an icon to your module, select a suitable PNG image and copy it to the
static/description/icon.pngpath within your module's directory.
Understanding Manifest Keys
The content of the manifest is a standard Python dictionary, comprising key-value pairs that provide detailed information about the module. The example manifest we just utilized includes the most frequently used and critical keys:
- name: This string defines the main title of the module as it appears in the Odoo interface.
- summary: A concise, one-line description or subtitle for the module, offering a quick overview of its purpose.
- description: A more detailed explanation of the module's functionality, typically written in plain text or ReStructuredText (RST) format. It is often enclosed within triple quotes to accommodate multi-line text in Python.
- author: A string indicating the name(s) of the module's creator(s). If multiple authors are involved, their names are commonly separated by commas within a single string.
- license: Specifies the license under which the module is distributed. Common options include AGPL-3, LGPL-3, "Other OSI approved license," and "Other proprietary."
- website: Provides a URL where users can find more information about the module or its authors.
- category: Used to classify modules into logical areas of interest. While a standard list of category names exists, new custom categories can also be defined here.
- version: Indicates the module's version number. This is often used by application stores to track and identify newer versions. It's good practice to explicitly state the Odoo target version (e.g., 11.0.1.0.0 or 11.0.1.0).
- depends: A Python list containing the technical names of other modules that this module directly relies upon. It is crucial to at least depend on the
basemodule if there are no other specific dependencies. Always ensure that any modules defining XML IDs, Views, or Models referenced by this module are included here to guarantee correct loading order and prevent potential errors. - data: A list of relative paths to data files (usually XML and CSV) that are loaded during the module's installation or upgrade. These paths are relative to the module's root directory.
- demo: A list of relative paths to files containing demonstration data. These files are only loaded if the database was initially created with the Demo Data flag enabled, making them useful for testing, training, or module evaluation.
The image used as the module icon is expected to be a PNG file located at static/description/icon.png.
It's important to note that Odoo typically undergoes significant changes between major versions. Consequently, modules developed for one major version may not be directly compatible with the next without requiring conversion and migration efforts. Therefore, verifying a module's targeted Odoo version before installation is a critical step.
Advanced Manifest Configuration
Beyond the primary keys, there are additional ways to manage the module description and other behaviors.
Instead of embedding a long description directly within the module manifest, it is possible to provide it in a separate file. Since Odoo version 8.0, this description can be specified via a README file, which can have either a .txt, .rst, or .md (Markdown) extension. Alternatively, an description/index.html file can be included within the module, and its content will override any description defined in the manifest file.
A few more commonly used keys in the manifest are:
- application: If set to
True, the module will be listed as a primary application within the Odoo interface. This is typically used for modules that represent a central functional area. - auto_install: When set to
True, this indicates a "glue" module, meaning it will be automatically installed once all of its declared dependencies have been installed. - installable: By default, this value is
True, signifying that the module is available for installation. Setting it toFalsewould prevent its installation.
Organizing the Addon Module File Structure
An addon module is a collection of various files, including code files and other assets such as XML configurations and images. While there is flexibility in placing most of these files within the module directory, Odoo adheres to certain conventions regarding the module structure, and it is highly recommended to follow these for consistency and best practice.
Initial Setup
At this stage, we assume you have an addon module directory that currently contains only the __init__.py and __manifest__.py files. For this recipe, we will continue to refer to our example module as local-addons/my_module.
Creating the Basic Module Skeleton
To establish the fundamental directory structure for your addon module, execute the following steps:
- Navigate into your module's directory and create the subdirectories designated for code files and other assets:
$ 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 - Edit the module's main
__init__.pyfile to ensure that the code within these new subdirectories is properly loaded by the module:from . import models from . import controllers
These steps will provide you with a basic, yet comprehensive, structure containing the most frequently used directories, resembling the following:
.
├── __init__.py
├── __manifest__.py
│
├── controllers
│ └── __init__.py
├── data
├── demo
├── i18n
├── models
│ └── __init__.py
├── security
├── static
│ └── description
└── views
Understanding the Module Structure
To provide further clarity, an Odoo addon module typically consists of three primary types of files:
- Python code: This code is loaded through the
__init__.pyfiles, which import individual.pyfiles and code subdirectories. Any subdirectory containing Python code, in turn, requires its own__init__.pyfile. - Data files: These files are declared within the
dataanddemokeys of the__manifest__.pyfile for loading. They commonly include XML and CSV files for defining the user interface, fixture data, and demonstration data. YAML files can also be used, potentially containing procedural instructions that execute upon module loading, enabling programmatic generation or updates of records rather than static XML definitions. - Web assets: This category encompasses elements like JavaScript code and libraries, CSS stylesheets, and QWeb/HTML templates, which are vital for the web interface. These assets are typically declared via an XML file that extends master templates to incorporate them into the web client or website pages.
The files within an addon module are conventionally organized into these specific directories:
- models/: Contains the backend code files responsible for creating Odoo Models and defining their associated business logic. It is generally recommended to use one file per Model, named similarly to the model itself (e.g.,
library_book.pyfor alibrary.bookmodel). - views/: Houses the XML files that define the user interface elements, such as actions, forms, lists, and other visual components. Similar to models, it is advisable to maintain one file per model. Filenames for website templates are expected to end with the
_templatesuffix. - data/: Holds other data files containing the module's initial data, distinct from demonstration data.
- demo/: Contains data files specifically for demonstration purposes. This data is invaluable for testing, training, or evaluating the module's functionalities.
- i18n/: This is the designated location where Odoo expects to find translation
.potand.pofiles. These translation files do not need to be explicitly mentioned in the manifest file. - security/: Stores data files that define access control lists. This typically includes an
ir.model.access.csvfile, and potentially an XML file for defining access Groups and Record Rules to implement row-level security. - controllers/: Contains the Python code files for website controllers, particularly relevant for modules that offer web-based features and functionalities.
- static/: This directory is where all web assets are expected to be placed. Unlike other directories,
static/is not merely a convention; only files located within this directory can be made accessible to Odoo web pages. While they don't need to be listed in the module manifest, they must be referenced within the web templates.
When incorporating new files into your module, always remember to declare them appropriately: data files in __manifest__.py and code files in __init__.py. Failing to do so will result in those files being ignored and not loaded by Odoo.
Adding Models to Your Module
Models are fundamental to Odoo, defining the data structures that underpin our business applications. This section will demonstrate how to integrate a basic model into an existing module.
For illustrative purposes, we will use a simple book library example. Our goal is to create a model that represents books, where each book possesses a name and an association with one or more authors.
Preparation
You should have a working module ready to be extended. For our explanation, we will use an empty module named my_module as our starting point.
Implementing a New Model
To add a new Model, we will create a Python file that describes it, and then upgrade our addon module (or install it if it's new). The paths mentioned below are relative to your addon module's location (e.g., ~/odoo-dev/local-addons/my_module/):
- Add a Python file named
library_book.pyto themodels/directory within your module, containing 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' ) - Next, create or modify the Python initialization file in the
models/__init__.pydirectory to ensure our new code file is loaded:from . import library_book - Edit the module's main Python initialization file (
my_module/__init__.py) to load the entiremodels/directory:from . import models - Finally, upgrade your Odoo module. This can be done either through the command line or via the 'Apps' menu in the user interface. If you monitor the server log during the upgrade, you should observe a line similar to this, confirming the database table creation or update:
odoo.modules.registry: module my_module: creating or updating database table
Following these steps, the new library.book model should be fully available in your Odoo instance. If you have the technical tools activated, you can verify its presence by navigating to Settings | Technical | Database Structure | Models.
Model Definition and Activation
Our initial action was to create a Python file dedicated to defining our new module's model. Odoo models are specialized Python objects that inherit from the core Odoo Model class, endowing them with ORM (Object-Relational Mapping) capabilities.
When a new model is defined, it is automatically registered in a central model registry. This centralized registration simplifies the process for other modules to later make modifications or extensions to it.
Models include several generic attributes, typically prefixed with an underscore. Among these, the most crucial is _name, which provides a unique internal identifier that will be used consistently throughout the Odoo instance to reference this specific model.
The fields of a model are defined as class attributes. We started by defining the name field, an instance of the Char type. It is highly beneficial for models to have this field, as it is, by default, used as the record's descriptive label when referenced from other models or in the user interface.
We also included an example of a relational field, author_ids. This field establishes a many-to-many relationship between Library Books and Partners (res.partner). This signifies that a single book can be attributed to multiple authors, and conversely, each author can have contributed to numerous books.
The next essential step is to ensure that our module is aware of this newly created Python file. This is achieved through the __init__.py files. Since we placed the code within the models/ subdirectory, the parent __init__.py file needs to import this directory. In turn, the models/__init__.py file should contain import statements for each of the Python code files residing within it (in our case, just one initially).
Any changes made to Odoo models are activated by upgrading the corresponding module. The Odoo server intelligently handles the translation of the model class definitions into the necessary database structure changes, ensuring data integrity and consistency.
While not explicitly demonstrated here, it is also within these Python files that business logic can be added. This is accomplished by either introducing new methods to the Model's class or by extending existing core methods, such as create() or write(), to customize their behavior.
Through this comprehensive guide, we have explored the foundational structure of an Odoo addon module and learned, step by step, how to successfully create a simple module from its inception.
