{"id":9657,"date":"2026-10-05T15:54:42","date_gmt":"2026-10-05T12:54:42","guid":{"rendered":"https:\/\/machinser.com\/?p=9657"},"modified":"2026-10-05T15:57:07","modified_gmt":"2026-10-05T12:57:07","slug":"odoo-19-module-structure-developers-handbook","status":"publish","type":"post","link":"https:\/\/machinser.com\/ar\/odoo-19-module-structure-developers-handbook\/","title":{"rendered":"Odoo 19 Module Structure Explained: The Practical Developer&#8217;s Handbook"},"content":{"rendered":"<p>When starting development, understanding the Odoo 19 module structure and how an Odoo project is organized is essential. <span class=\"citation-10 citation-end-10\">An Odoo development environment is more than just the Odoo source code. Depending on how the project is set up, it may contain the Odoo source, standard addons, custom addons, a Python virtual environment, configuration files, and other project-specific directo<\/span>ries.<\/p>\n<p>For beginners, one of the most common questions is: Where should I keep my custom modules, virtual environment, and configuration files?<\/p>\n<p>Odoo does not enforce one single project structure. These components can be arranged in different ways, as long as Odoo is correctly configured to locate the required addon directories and configuration files.<\/p>\n<p>In this guide, we will explore the Odoo 19 project structure, different ways to organize an Odoo development environment, how addons_path works, the main components of an Odoo module, and the purpose of __init__.py and __manifest__.py.<\/p>\n<h2>1. Understanding the Odoo 19 Module Structure &amp; Project Layout<\/h2>\n<p>A typical Odoo 19 development environment can contain several important directories.<\/p>\n<p>odoo-dev\/<br \/>\n\u251c\u2500\u2500 config\/<br \/>\n\u251c\u2500\u2500 custom_addon\/<br \/>\n\u251c\u2500\u2500 odoo19\/<br \/>\n\u2514\u2500\u2500 venv\/<\/p>\n<p>Here:<\/p>\n<ul>\n<li>odoo19\/ &#8211; Odoo source code.<\/li>\n<li>custom_addon\/ &#8211; custom Odoo modules.<\/li>\n<li>venv\/ &#8211; Python virtual environment.<\/li>\n<li>config\/ &#8211; configuration files.<\/li>\n<\/ul>\n<p>This is the structure used in our development environment. The important thing to understand is that these directories do not have to be located inside the Odoo source directory.<\/p>\n<figure id=\"attachment_9661\" aria-describedby=\"caption-attachment-9661\" style=\"width: 960px\" class=\"wp-caption aligncenter\"><img fetchpriority=\"high\" decoding=\"async\" class=\"wp-image-9661\" src=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/1-300x173.jpeg\" alt=\"\" width=\"960\" height=\"555\" srcset=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/1-300x173.jpeg 300w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/1-1024x592.jpeg 1024w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/1-768x444.jpeg 768w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/1-1536x888.jpeg 1536w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/1-18x10.jpeg 18w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/1.jpeg 1600w\" sizes=\"(max-width: 960px) 100vw, 960px\" \/><figcaption id=\"caption-attachment-9661\" class=\"wp-caption-text\">Figure 1. Example Odoo 19 development environment with separate configuration, custom addon, Odoo source, and virtual environment directories.<\/figcaption><\/figure>\n<p>&nbsp;<\/p>\n<h2>2. Understanding the Odoo 19 Source Directory<\/h2>\n<p>In Odoo 19 Module Structure the Odoo source directory contains the actual Odoo framework. In a source installation, it commonly contains the standard addons, the Odoo Python package, setup files, requirements, and the odoo-bin entry point.<\/p>\n<p>odoo19\/<br \/>\n\u251c\u2500\u2500 addons\/<br \/>\n\u251c\u2500\u2500 odoo\/<br \/>\n\u251c\u2500\u2500 setup\/<br \/>\n\u251c\u2500\u2500 debian\/<br \/>\n\u251c\u2500\u2500 doc\/<br \/>\n\u251c\u2500\u2500 requirements.txt<br \/>\n\u251c\u2500\u2500 odoo-bin<br \/>\n\u2514\u2500\u2500 &#8230;<\/p>\n<p>The `odoo-bin` file is used to start the Odoo server when running Odoo from source. The `addons` directory inside the Odoo source contains standard Odoo modules.<\/p>\n<h2>3. Where Can Custom Addons Be Placed?<\/h2>\n<p>Custom modules can be placed in a separate directory rather than directly inside Odoo&#8217;s standard `addons` directory.<\/p>\n<p>odoo-dev\/<br \/>\n\u251c\u2500\u2500 odoo19\/<br \/>\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 addons\/<br \/>\n\u2502<br \/>\n\u2514\u2500\u2500 custom_addon\/<br \/>\n\u2514\u2500\u2500 employee\/<\/p>\n<p>The directory name itself is not important. It can be called `custom_addons`, `custom_addon`, `custom_modules`, `extra_addons`, or another meaningful name. What matters is that the directory containing the custom modules is included in the Odoo `addons_path`.<\/p>\n<h2>4. Does Everything Have to Be Inside the Odoo Folder?<\/h2>\n<p>No. This is one of the most important things to understand when setting up Odoo.<\/p>\n<p>odoo-dev\/<br \/>\n\u251c\u2500\u2500 config\/<br \/>\n\u251c\u2500\u2500 custom_addon\/<br \/>\n\u251c\u2500\u2500 odoo19\/<br \/>\n\u2514\u2500\u2500 venv\/<\/p>\n<p>The Odoo source is in &#8216;<a href=\"https:\/\/www.odoo.com\/\" target=\"_blank\" rel=\"noopener\">odoo19<\/a>&#8216;, while the custom addons, virtual environment, and configuration are maintained separately. Another developer may prefer to keep everything under one Odoo project directory. Both approaches can work.<\/p>\n<h2>5. Different Ways to Arrange an Odoo Development Environment<\/h2>\n<h3>Structure A \u2014 Separate Project Components<\/h3>\n<p>odoo-dev\/<br \/>\n\u251c\u2500\u2500 config\/<br \/>\n\u251c\u2500\u2500 custom_addon\/<br \/>\n\u251c\u2500\u2500 odoo19\/<br \/>\n\u2514\u2500\u2500 venv\/<\/p>\n<p>This provides a clear separation between the Odoo source, custom code, configuration, and Python environment.<\/p>\n<figure id=\"attachment_9662\" aria-describedby=\"caption-attachment-9662\" style=\"width: 960px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" class=\"wp-image-9662\" src=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/2.1-300x170.png\" alt=\"Odoo 19 Module Structure\" width=\"960\" height=\"545\" srcset=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/2.1-300x170.png 300w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/2.1-18x10.png 18w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/2.1.png 437w\" sizes=\"(max-width: 960px) 100vw, 960px\" \/><figcaption id=\"caption-attachment-9662\" class=\"wp-caption-text\">Figure 2.1 Odoo 19 project arrangement where custom addons and the virtual environment are maintained outside the Odoo project directory.<\/figcaption><\/figure>\n<p>Advantages:<\/p>\n<ul>\n<li>Easy to understand.<\/li>\n<li>Clean project structure.<\/li>\n<li>Custom modules are separated from Odoo source.<\/li>\n<li>Configuration is separated.<\/li>\n<li>Virtual environment is isolated.<\/li>\n<\/ul>\n<h3>Structure B \u2014 Everything Inside One Odoo Project Directory<\/h3>\n<p>odoo19\/<br \/>\n\u251c\u2500\u2500 addons\/<br \/>\n\u251c\u2500\u2500 custom_addons\/<br \/>\n\u251c\u2500\u2500 venv\/<br \/>\n\u251c\u2500\u2500 odoo.conf<br \/>\n\u251c\u2500\u2500 odoo-bin<br \/>\n\u2514\u2500\u2500 &#8230;<\/p>\n<figure id=\"attachment_9663\" aria-describedby=\"caption-attachment-9663\" style=\"width: 960px\" class=\"wp-caption aligncenter\"><img decoding=\"async\" class=\"wp-image-9663\" src=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/2.2-225x300.png\" alt=\"Odoo 19 Module Structure\" width=\"960\" height=\"1279\" srcset=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/2.2-225x300.png 225w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/2.2-9x12.png 9w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/2.2.png 431w\" sizes=\"(max-width: 960px) 100vw, 960px\" \/><figcaption id=\"caption-attachment-9663\" class=\"wp-caption-text\">Figure 2.2 An alternative Odoo 19 project arrangement where custom addons and the virtual environment are maintained inside the Odoo project directory.<\/figcaption><\/figure>\n<p>&nbsp;<\/p>\n<h2>6. Where Should the Virtual Environment Be?<\/h2>\n<p>The Python virtual environment can be placed inside or outside the Odoo source directory. It is used to isolate the Python packages required by Odoo.<\/p>\n<p>odoo-dev\/<br \/>\n\u251c\u2500\u2500 odoo19\/<br \/>\n\u251c\u2500\u2500 custom_addon\/<br \/>\n\u251c\u2500\u2500 config\/<br \/>\n\u2514\u2500\u2500 venv\/<\/p>\n<p>On Linux, a typical virtual environment contains bin\/, include\/, lib\/, and pyvenv.cfg. The exact location is a project organization choice.<\/p>\n<h2>7. Where Should the Odoo Configuration File Be?<\/h2>\n<p>The Odoo configuration file is commonly named odoo.conf. It contains server configuration such as database connection, addon paths, HTTP port, logging configuration, and other Odoo options.<\/p>\n<p>odoo-dev\/<br \/>\n\u251c\u2500\u2500 config\/<br \/>\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 odoo.conf<br \/>\n\u251c\u2500\u2500 custom_addon\/<br \/>\n\u251c\u2500\u2500 odoo19\/<br \/>\n\u2514\u2500\u2500 venv\/<\/p>\n<p>The configuration can also be kept inside the Odoo project. Keeping configuration in a separate &#8216;config&#8217; directory can make larger projects easier to organize.<\/p>\n<h2>8. Configuring addons_path<\/h2>\n<p>The most important setting for loading custom modules is addons_path. It tells Odoo where it should search for modules.<\/p>\n<p>addons_path = \/home\/user\/odoo-dev\/odoo19\/addons,\/home\/user\/odoo-dev\/custom_addon<\/p>\n<p>Here, Odoo searches two locations:<\/p>\n<ul>\n<li>The standard Odoo addons directory.<\/li>\n<li>The custom addons directory.<\/li>\n<\/ul>\n<figure id=\"attachment_9664\" aria-describedby=\"caption-attachment-9664\" style=\"width: 960px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9664\" src=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/3-300x87.png\" alt=\"\" width=\"960\" height=\"280\" srcset=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/3-300x87.png 300w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/3-768x224.png 768w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/3-18x5.png 18w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/3.png 971w\" sizes=\"(max-width: 960px) 100vw, 960px\" \/><figcaption id=\"caption-attachment-9664\" class=\"wp-caption-text\">Figure 4. Odoo 19 configuration file showing the standard and custom addon paths. Password values have been redacted for security.<\/figcaption><\/figure>\n<p>&nbsp;<\/p>\n<h2>9. How to Add a Custom Addon Path<\/h2>\n<p>Suppose your project looks like this:<\/p>\n<p>odoo-dev\/<br \/>\n\u251c\u2500\u2500 odoo19\/<br \/>\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 addons\/<br \/>\n\u2502<br \/>\n\u2514\u2500\u2500 custom_addon\/<br \/>\n\u2514\u2500\u2500 employee\/<\/p>\n<p>The configuration should point to the addon directory:<\/p>\n<p>addons_path = \/path\/to\/odoo19\/addons,\/path\/to\/custom_addon<\/p>\n<p>The important concept is: add the parent directory that contains your custom modules, not the individual module directory.<\/p>\n<p>custom_addon\/<br \/>\n\u251c\u2500\u2500 employee\/<br \/>\n\u251c\u2500\u2500 sale_customization\/<br \/>\n\u251c\u2500\u2500 property_booking\/<br \/>\n\u2514\u2500\u2500 custom_reports\/<\/p>\n<p>Odoo can discover all of these modules through the single `custom_addon` path.<\/p>\n<h2>10. Using Multiple Addon Paths<\/h2>\n<p>Odoo can search multiple addon directories.<\/p>\n<p>addons_path = \/path\/to\/odoo\/addons,\/path\/to\/custom_addons,\/path\/to\/third_party_addons<\/p>\n<p>This can be useful for separating:<\/p>\n<ul>\n<li>Standard Odoo addons.<\/li>\n<li>Company custom addons.<\/li>\n<li>Third-party addons.<\/li>\n<\/ul>\n<p>odoo-dev\/<br \/>\n\u251c\u2500\u2500 odoo19\/<br \/>\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 addons\/<br \/>\n\u251c\u2500\u2500 custom_addons\/<br \/>\n\u2514\u2500\u2500 third_party_addons\/<\/p>\n<h2>11. What Is an Odoo Module?<\/h2>\n<p>An <a href=\"https:\/\/www.odoo.com\/documentation\/19.0\/developer\/tutorials\/backend.html\" target=\"_blank\" rel=\"noopener\">Odoo module<\/a> is a collection of files that adds or extends functionality in Odoo. A module can contain Python code, XML views, CSV files, security rules, data files, reports, JavaScript, CSS, images, and website assets.<\/p>\n<h2>12. Basic Odoo 19 Module Structure<\/h2>\n<figure id=\"attachment_9666\" aria-describedby=\"caption-attachment-9666\" style=\"width: 960px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9666\" src=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/4-300x152.png\" alt=\"\" width=\"960\" height=\"487\" srcset=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/4-300x152.png 300w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/4-18x9.png 18w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/4.png 394w\" sizes=\"(max-width: 960px) 100vw, 960px\" \/><figcaption id=\"caption-attachment-9666\" class=\"wp-caption-text\">Figure 5. Basic Odoo custom module structure containing data, models, security, views, and module initialization files.<\/figcaption><\/figure>\n<p>&nbsp;<\/p>\n<h2>13. Common Components of an Odoo Module<\/h2>\n<h3>models\/<\/h3>\n<p>The `models` directory contains Python files that define or extend Odoo models. These files can contain models, fields, business logic, computed fields, methods, constraints, and model inheritance.<\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9665\" src=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/4.1-300x150.png\" alt=\"\" width=\"960\" height=\"481\" srcset=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/4.1-300x150.png 300w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/4.1-18x9.png 18w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/4.1.png 427w\" sizes=\"(max-width: 960px) 100vw, 960px\" \/><\/p>\n<p><em>Figure 6. Python model files inside the models directory of an Odoo custom module.<\/em><\/p>\n<h3>views\/<\/h3>\n<p>The `views` directory contains XML files that define the Odoo user interface, including list, form, kanban, search, calendar views, actions, and menus.<\/p>\n<p>views\/<br \/>\n\u251c\u2500\u2500 employee_views.xml<br \/>\n\u2514\u2500\u2500 sale_order.xml<\/p>\n<h3>security\/<\/h3>\n<p>Security files define who can access models and records. A common file is `ir.model.access.csv`, which can control read, create, write, and delete permissions.<\/p>\n<p>security\/<br \/>\n\u2514\u2500\u2500 ir.model.access.csv<\/p>\n<h3>data\/<\/h3>\n<p>The `data` directory can contain predefined data and configuration records such as sequences, email templates, scheduled actions, and other configuration records.<\/p>\n<p>data\/<br \/>\n\u2514\u2500\u2500 employee_sequence.xml<\/p>\n<h2>14. Understanding __init__.py<\/h2>\n<p>The `__init__.py` file is used to initialize Python packages and import the Python components of the module.<\/p>\n<p>employee\/<br \/>\n\u251c\u2500\u2500 __init__.py<br \/>\n\u2514\u2500\u2500 models\/<br \/>\n\u251c\u2500\u2500 __init__.py<br \/>\n\u251c\u2500\u2500 employee.py<br \/>\n\u2514\u2500\u2500 sale_order.py<\/p>\n<p>The root &#8216;__init__.py&#8217; imports the &#8216;models&#8217; package:<\/p>\n<p>from . import models<\/p>\n<figure id=\"attachment_9668\" aria-describedby=\"caption-attachment-9668\" style=\"width: 960px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9668\" src=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/5-300x99.png\" alt=\"\" width=\"960\" height=\"318\" srcset=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/5-300x99.png 300w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/5-1024x340.png 1024w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/5-768x255.png 768w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/5-18x6.png 18w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/5.png 1143w\" sizes=\"(max-width: 960px) 100vw, 960px\" \/><figcaption id=\"caption-attachment-9668\" class=\"wp-caption-text\">Figure 7. Root __init__.py importing the models package in an Odoo custom module.<\/figcaption><\/figure>\n<p>&nbsp;<\/p>\n<h2>15. The models\/__init__.py File<\/h2>\n<p>The &#8216;models&#8217; directory has its own &#8216;__init__.py&#8217; file. For example:<\/p>\n<p>from . import employee<br \/>\nfrom . import sale_order<\/p>\n<figure id=\"attachment_9667\" aria-describedby=\"caption-attachment-9667\" style=\"width: 960px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9667\" src=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/5.1-300x117.png\" alt=\"\" width=\"960\" height=\"376\" srcset=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/5.1-300x117.png 300w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/5.1-1024x401.png 1024w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/5.1-768x301.png 768w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/5.1-18x7.png 18w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/5.1.png 1167w\" sizes=\"(max-width: 960px) 100vw, 960px\" \/><figcaption id=\"caption-attachment-9667\" class=\"wp-caption-text\">Figure 8. The models\/__init__.py file importing individual Python model files.<\/figcaption><\/figure>\n<p>&nbsp;<\/p>\n<p>The import chain can therefore be understood as:<\/p>\n<p>employee\/__init__.py<br \/>\n\u2193<br \/>\nmodels<br \/>\n\u2193<br \/>\nmodels\/__init__.py<br \/>\n\u2193<br \/>\nemployee.py<br \/>\nsale_order.py<\/p>\n<h2>16. Understanding __manifest__.py<\/h2>\n<p>The second essential file in an Odoo module is &#8216;__manifest__.py&#8217;. The manifest provides information about the module and tells Odoo which files need to be loaded.<\/p>\n<p>{<br \/>\n&#8220;name&#8221;: &#8220;Employee Management&#8221;,<br \/>\n&#8220;summary&#8221;: &#8220;Manage employee records and contact details&#8221;,<br \/>\n&#8220;version&#8221;: &#8220;19.0.1.0.0&#8221;,<br \/>\n&#8220;category&#8221;: &#8220;Human Resources&#8221;,<br \/>\n&#8220;license&#8221;: &#8220;LGPL-3&#8221;,<\/p>\n<p>&#8220;depends&#8221;: [<br \/>\n&#8220;base&#8221;,<br \/>\n&#8220;crm&#8221;,<br \/>\n&#8220;sale&#8221;,<br \/>\n&#8220;account&#8221;,<br \/>\n],<\/p>\n<p>&#8220;data&#8221;: [<br \/>\n&#8220;security\/ir.model.access.csv&#8221;,<br \/>\n&#8220;data\/employee_sequence.xml&#8221;,<br \/>\n&#8220;views\/sale_order.xml&#8221;,<br \/>\n&#8220;views\/employee_views.xml&#8221;,<br \/>\n],<\/p>\n<p>&#8220;installable&#8221;: True,<br \/>\n&#8220;application&#8221;: True,<br \/>\n}<\/p>\n<figure id=\"attachment_9669\" aria-describedby=\"caption-attachment-9669\" style=\"width: 960px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9669\" src=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/6-300x105.png\" alt=\"\" width=\"960\" height=\"335\" srcset=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/6-300x105.png 300w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/6-1024x357.png 1024w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/6-768x268.png 768w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/6-18x6.png 18w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/6.png 1234w\" sizes=\"(max-width: 960px) 100vw, 960px\" \/><figcaption id=\"caption-attachment-9669\" class=\"wp-caption-text\">Figure 9. Example __manifest__.py defining metadata, dependencies, and data files of an Odoo 19 custom module.<\/figcaption><\/figure>\n<p>&nbsp;<\/p>\n<h2>17. Important Parameters in __manifest__.py<\/h2>\n<h3>name<\/h3>\n<p>&#8220;name&#8221;: &#8220;Employee Management&#8221;,<\/p>\n<p>Defines the display name of the module.<\/p>\n<h3>summary<\/h3>\n<p>&#8220;summary&#8221;: &#8220;Manage employee records and contact details&#8221;,<\/p>\n<p>Provides a short description of the module.<\/p>\n<h3>version<\/h3>\n<p>&#8220;version&#8221;: &#8220;19.0.1.0.0&#8221;,<\/p>\n<p>Identifies the module version. For Odoo 19 modules, the version commonly begins with 19.0.<\/p>\n<h3>category<\/h3>\n<p>&#8220;category&#8221;: &#8220;Human Resources&#8221;,<\/p>\n<p>Specifies the category under which the module belongs.<\/p>\n<h3>license<\/h3>\n<p>&#8220;license&#8221;: &#8220;LGPL-3&#8221;,<\/p>\n<p>Specifies the license under which the module is distributed.<\/p>\n<h3>depends<\/h3>\n<p>&#8220;depends&#8221;: [<br \/>\n&#8220;base&#8221;,<br \/>\n&#8220;crm&#8221;,<br \/>\n&#8220;sale&#8221;,<br \/>\n&#8220;account&#8221;,<br \/>\n],<\/p>\n<p>Defines the modules that must be available before the custom module can be loaded. If a custom module extends functionality from another Odoo module, that dependency should be declared here.<\/p>\n<h3>data<\/h3>\n<p>&#8220;data&#8221;: [<br \/>\n&#8220;security\/ir.model.access.csv&#8221;,<br \/>\n&#8220;data\/employee_sequence.xml&#8221;,<br \/>\n&#8220;views\/sale_order.xml&#8221;,<br \/>\n&#8220;views\/employee_views.xml&#8221;,<br \/>\n],<\/p>\n<p>Tells Odoo which XML and CSV files should be loaded. The order can be important when one file depends on records created by another file.<\/p>\n<h3>installable<\/h3>\n<p>&#8220;installable&#8221;: True,<\/p>\n<p>Determines whether the module can be installed.<\/p>\n<h3>application<\/h3>\n<p>&#8220;application&#8221;: True,<\/p>\n<p>Indicates that the module is treated as an application. Not every custom module needs to be a standalone application; some are extensions of existing Odoo applications.<\/p>\n<h2>18. Putting the Module Components Together<\/h2>\n<p>employee\/<br \/>\n\u2502<br \/>\n\u251c\u2500\u2500 data\/<br \/>\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 employee_sequence.xml<br \/>\n\u2502<br \/>\n\u251c\u2500\u2500 models\/<br \/>\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 __init__.py<br \/>\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 employee.py<br \/>\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 sale_order.py<br \/>\n\u2502<br \/>\n\u251c\u2500\u2500 security\/<br \/>\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 ir.model.access.csv<br \/>\n\u2502<br \/>\n\u251c\u2500\u2500 views\/<br \/>\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 employee_views.xml<br \/>\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 sale_order.xml<br \/>\n\u2502<br \/>\n\u251c\u2500\u2500 __init__.py<br \/>\n\u2514\u2500\u2500 __manifest__.py<\/p>\n<figure id=\"attachment_9670\" aria-describedby=\"caption-attachment-9670\" style=\"width: 960px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-9670\" src=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/7-300x183.png\" alt=\"\" width=\"960\" height=\"585\" srcset=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/7-300x183.png 300w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/7-18x12.png 18w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/7.png 568w\" sizes=\"(max-width: 960px) 100vw, 960px\" \/><figcaption id=\"caption-attachment-9670\" class=\"wp-caption-text\">Figure 10. Complete example of an Odoo 19 custom module with data, models, security, views, __init__.py, and __manifest__.py.<\/figcaption><\/figure>\n<p>&nbsp;<\/p>\n<h2>19. How the Main Files Work Together<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-9658\" src=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/Odoo-Module-Structure-Infographic-300x200.png\" alt=\"\" width=\"960\" height=\"640\" srcset=\"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/Odoo-Module-Structure-Infographic-300x200.png 300w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/Odoo-Module-Structure-Infographic-1024x683.png 1024w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/Odoo-Module-Structure-Infographic-768x512.png 768w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/Odoo-Module-Structure-Infographic-18x12.png 18w, https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/Odoo-Module-Structure-Infographic.png 1536w\" sizes=\"(max-width: 960px) 100vw, 960px\" \/><\/p>\n<p>The manifest describes the module and tells Odoo which files need to be loaded. The `__init__.py` files make the Python components available. The models contain business logic, views define the user interface, security controls access, and data contains predefined or configuration data.<\/p>\n<h2>20. Recommended Odoo 19 Development Structure<\/h2>\n<p>odoo-dev\/<br \/>\n\u2502<br \/>\n\u251c\u2500\u2500 config\/<br \/>\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 odoo.conf<br \/>\n\u2502<br \/>\n\u251c\u2500\u2500 custom_addons\/<br \/>\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 employee\/<br \/>\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 custom_sales\/<br \/>\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 custom_reports\/<br \/>\n\u2502<br \/>\n\u251c\u2500\u2500 odoo19\/<br \/>\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 addons\/<br \/>\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 odoo\/<br \/>\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 odoo-bin<br \/>\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 requirements.txt<br \/>\n\u2502<br \/>\n\u2514\u2500\u2500 venv\/<\/p>\n<p>[options]<\/p>\n<p>addons_path = \/path\/to\/odoo19\/addons,\/path\/to\/custom_addons<\/p>\n<p>This arrangement provides a clear separation between Odoo source code, custom business modules, the Python environment, and configuration. For a development environment, this is a practical and easy-to-maintain approach.<\/p>\n<h2>21. Common Mistakes to Avoid<\/h2>\n<h4>Adding the wrong addon path<\/h4>\n<p>If your module is inside `custom_addons\/employee\/`, the configuration should normally point to `custom_addons\/`, rather than directly to the `employee` module.<\/p>\n<h4>Forgetting __init__.py<\/h4>\n<p>Python files inside the module need to be imported through the appropriate initialization files.<\/p>\n<h4>Forgetting files in the manifest<\/h4>\n<p>Creating an XML or CSV file does not automatically mean Odoo will load it. The file generally needs to be included in the appropriate manifest section.<\/p>\n<h4>Forgetting dependencies<\/h4>\n<p>If your module uses functionality from another Odoo module, that dependency should be declared in `depends`.<\/p>\n<h4>Modifying Odoo core unnecessarily<\/h4>\n<p>Keep business-specific functionality in custom modules instead of modifying standard Odoo source files whenever possible.<\/p>\n<h2>Conclusion<\/h2>\n<p>Understanding the Odoo 19 project and module structure is an important first step before starting custom development.<\/p>\n<p>The Odoo source code, custom addons, virtual environment, and configuration file do not have to be stored in one specific location. Developers can organize them according to the needs of their project.<\/p>\n<p>The most important connection is the `addons_path` in the Odoo configuration file. It tells Odoo where to search for standard, custom, and third-party modules.<\/p>\n<p>Inside a custom module, different components have different responsibilities:<\/p>\n<ul>\n<li>&#8216;models\/&#8217; \u2014 Python models and business logic<\/li>\n<li>&#8216;views\/&#8217; \u2014 User interface definitions<\/li>\n<li>&#8216;security\/&#8217; \u2014 Access rights and security rules<\/li>\n<li>&#8216;data\/&#8217; \u2014 Predefined and configuration data<\/li>\n<li>&#8216;__init__.py&#8217; \u2014 Imports Python components<\/li>\n<li>&#8216;__manifest__.py&#8217; \u2014 Defines module metadata, dependencies, and files to load<\/li>\n<\/ul>\n<p>Once this structure is understood, the next step is to create a custom module and see how these components work together in practice.<\/p>\n<h2>Frequently Asked Questions<\/h2>\n<h4>Does Odoo require custom modules to be inside the Odoo folder?<\/h4>\n<p>No. Custom modules can be stored in a separate directory as long as that directory is included in the Odoo `addons_path`.<\/p>\n<h4>Can I keep the virtual environment outside the Odoo source folder?<\/h4>\n<p>Yes. The Python virtual environment can be placed separately from the Odoo source.<\/p>\n<h4>Can I have multiple custom addon directories?<\/h4>\n<p>Yes. Multiple addon directories can be specified in `addons_path`, separated by commas.<\/p>\n<h4>What is the purpose of __init__.py?<\/h4>\n<p>It imports the Python packages and files that need to be loaded as part of the module.<\/p>\n<h4>What is the purpose of __manifest__.py?<\/h4>\n<p>It describes the Odoo module and defines important information such as its name, version, dependencies, data files, and installation options.<\/p>\n<h4>What is the difference between addons_path and a module path?<\/h4>\n<p>`addons_path` points to directories containing Odoo modules. Individual module directories are discovered inside those addon paths.<\/p>\n<h4>Do all Odoo modules need models, views, security, and data directories?<\/h4>\n<p>No. The structure depends on the functionality of the module. A simple module may need only some of these components.<\/p>\n<p><a href=\"https:\/\/machinser.com\">Machinser<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>When starting development, understanding the Odoo 19 module structure and how an Odoo project is organized is essential. An Odoo development environment is more than just the Odoo source code. Depending on how the project is set up, it may contain the Odoo source, standard addons, custom addons, a Python virtual environment, configuration files, and [&hellip;]<\/p>\n","protected":false},"author":5,"featured_media":9672,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[318,330,313,192],"tags":[],"class_list":["post-9657","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-odoo-19","category-custome-addons","category-odoo-configuration","category-odoo-erp"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/machinser.com\/wp-content\/uploads\/2026\/10\/Odoo-19-Module-Structure-Explained.png","_links":{"self":[{"href":"https:\/\/machinser.com\/ar\/wp-json\/wp\/v2\/posts\/9657","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/machinser.com\/ar\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/machinser.com\/ar\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/machinser.com\/ar\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/machinser.com\/ar\/wp-json\/wp\/v2\/comments?post=9657"}],"version-history":[{"count":6,"href":"https:\/\/machinser.com\/ar\/wp-json\/wp\/v2\/posts\/9657\/revisions"}],"predecessor-version":[{"id":9675,"href":"https:\/\/machinser.com\/ar\/wp-json\/wp\/v2\/posts\/9657\/revisions\/9675"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/machinser.com\/ar\/wp-json\/wp\/v2\/media\/9672"}],"wp:attachment":[{"href":"https:\/\/machinser.com\/ar\/wp-json\/wp\/v2\/media?parent=9657"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/machinser.com\/ar\/wp-json\/wp\/v2\/categories?post=9657"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/machinser.com\/ar\/wp-json\/wp\/v2\/tags?post=9657"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}