Quick Answer
__manifest__.py is the most important file in your Odoo module—8 lines of code that control installation, dependencies, data loading, and hooks. Required fields: name (human-readable), version (18.0.1.0.0 format = Odoo version + Major.Minor.Patch), license (AGPL-3 for public). depends field critical: List ALL dependencies or module crashes (inherit from inventory? must list 'inventory'). data files load order matters: data before views (views reference data = load data first). Hooks: pre_init (run before install, check prerequisites), post_init (run after install, create defaults), uninstall (cleanup). external_dependencies: Python packages (requests, pandas) or binaries (convert, pdflatex). Wrong manifest = 4+ hours debugging 1-line fix.
The Manifest Problem
Your developer just created a new custom module. They send you the code. You look at the folder structure and see various files: __manifest__.py, models/, views/, security/.
You open __manifest__.py and see this:
{
'name': 'Custom Inventory Alerts',
'version': '18.0.1.0.0',
'category': 'Inventory',
'depends': ['inventory', 'mail'],
'data': ['security/ir.model.access.csv', 'views/alert_views.xml'],
'installable': True,
'application': False,
}
We've implemented 150+ Odoo modules. The ones with proper __manifest__.py files? Zero installation issues. The ones with sloppy manifests? We've spent hours debugging "module won't install" problems that traced back to bad manifest configuration.
What is __manifest__.py?
A Python dictionary (key-value pairs) that tells Odoo metadata about your module. Metadata = information about information. It's not the module code itself. It's information ABOUT the module.
Why It Matters
Odoo reads the manifest file first. BEFORE it tries to load your Python code. If the manifest is wrong:
✗ Module won't appear in the Apps list (can't install)
✗ Module installs with missing dependencies (crashes on startup)
✗ Module doesn't load data files (configuration is incomplete)
✗ Module conflicts with other modules (version incompatibility)
Real Scenario: Missing Dependency
You build a custom module that depends on the inventory module. You forget to add 'inventory' to the depends field in __manifest__.py.
What happens: User tries to install → Odoo tries to load it → Your code tries to inherit from inventory models → Inventory models don't exist yet (not loaded) → Installation FAILS with cryptic error: "Model not found"
What should have happened: You put 'depends': ['inventory'] in __manifest__.py. Odoo loads inventory first. Then loads your module. Zero errors.
Cost of bad manifest: 4+ hours debugging a 1-line fix.
Section 1: Module Identity (What Your Module Is)
name (Required)
Human-readable name of your module. What users see in the Odoo Apps list.
'name': 'Low Stock Inventory Alerts',
version (Required)
Module version. Must follow Odoo versioning convention: [Odoo-Version].[Major].[Minor].[Patch]
'version': '18.0.1.0.0',
# Breaking down this version:
# 18.0 = For Odoo 18 (which version of Odoo is this module built for?)
# 1 = Major version (1 = first major release)
# 0 = Minor version (0 = no feature additions yet)
# 0 = Patch version (0 = no bug fixes yet)
Version Progression Example
| Version | Meaning |
|---|---|
| 18.0.1.0.0 | Initial release |
| 18.0.1.0.1 | Bug fix (patch) |
| 18.0.1.1.0 | New feature (minor) |
| 18.0.2.0.0 | Breaking changes (major) |
| 19.0.1.0.0 | Ported to Odoo 19 (new Odoo version) |
license (Required)
Open source license. Odoo requires all modules to be open source.
'license': 'AGPL-3', # Most common, required for public modules
# Valid licenses:
# 'AGPL-3' (Most common, required for public modules)
# 'LGPL-3'
# 'MIT'
# 'OEEL-1' (Odoo Enterprise Edition License, only for Odoo Inc.)
Section 2: Module Organization (What Files to Load)
depends (Optional, Default: [])
List of modules that MUST be installed before your module. Critical field.
'depends': ['inventory', 'mail', 'account'],
Real example: You build a custom purchase order module. Your code does:
class PurchaseOrder(models.Model):
_inherit = 'purchase.order' # Inheriting from purchase module
custom_field = fields.Char()
Your manifest MUST include:
'depends': ['purchase'], # So Odoo loads purchase BEFORE your module
data (Optional, Default: [])
List of XML/CSV files to load with the module. These are NOT demo data, they're REQUIRED configuration.
'data': [
'security/ir.model.access.csv', # Security rules
'views/alert_views.xml', # User interface
'data/alert_categories.xml', # Default data
'reports/alert_report.xml', # Reports
],
Load order matters: Files are loaded in the order listed. If views/alert_views.xml references data/alert_categories.xml, load data FIRST.
'data': [
'data/alert_categories.xml', # Load first
'views/alert_views.xml', # Load second
],
external_dependencies (Optional)
External Python packages or system binaries required.
'external_dependencies': {
'python': ['requests', 'pandas'], # Python packages from pip
'bin': ['convert', 'pdflatex'], # System binaries
},
Critical: Odoo doesn't auto-install them. The user MUST install them manually (via pip). If they don't, module still crashes.
Section 3: Installation Behavior
installable (Optional, Default: True)
Whether the module can be installed from the Odoo UI.
'installable': True, # Module can be installed
# Set to False if:
# - Module is incomplete/in development
# - Module has broken dependencies
# - Module is for internal testing only
application (Optional, Default: False)
Whether the module is a complete app (shows in Apps main list) or an add-on (shows under parent modules).
'application': True, # Shows as main app
# vs
'application': False, # Shows under a parent module (e.g., under Inventory)
# Examples:
# - Inventory module: application=True (standalone app)
# - Low Stock Alerts: application=False (extends Inventory)
auto_install (Optional, Default: False)
Auto-install the module if all dependencies are installed.
'auto_install': True,
# When to use:
# - Module is essential infrastructure
# - Module provides base functionality other modules depend on
# - Module should always be installed
Section 4: Hooks (Run Code During Installation)
pre_init_hook (Optional)
Function to run BEFORE module installation.
# In __manifest__.py
'pre_init_hook': 'pre_init_setup',
# Then in __init__.py:
def pre_init_setup(cr):
"""Run before module installation."""
cr.execute("""
CREATE TABLE IF NOT EXISTS custom_table (
id SERIAL PRIMARY KEY,
name VARCHAR(255)
);
""")
post_init_hook (Optional)
Function to run AFTER module installation.
# In __manifest__.py
'post_init_hook': 'post_init_setup',
# Then in __init__.py:
def post_init_setup(cr, registry):
"""Run after module installation."""
# Load initial data
# Send notification to admin
# Trigger workflows
pass
uninstall_hook (Optional)
Function to run when module is UNINSTALLED.
# In __manifest__.py
'uninstall_hook': 'uninstall_cleanup',
# Then in __init__.py:
def uninstall_cleanup(cr, registry):
"""Run on module uninstall."""
# Clean up custom tables
# Archive related records
# Notify admin
cr.execute("DROP TABLE IF EXISTS custom_table;")
Real-World Examples
Example 1: Simple Extension Module
{
'name': 'Sales Order Extensions',
'version': '18.0.1.0.0',
'category': 'Sales',
'summary': 'Add custom fields to sales orders',
'description': '''
Extends sales orders with:
- Gift message field
- Rush processing flag
- Customer special instructions
''',
'author': 'Your D2C Brand',
'website': 'https://yourshop.com',
'license': 'AGPL-3',
'depends': ['sale'], # Depends on Sales module
'data': [
'views/sale_order_extension_views.xml',
],
'installable': True,
'application': False, # Extension, not standalone app
'auto_install': False,
}
Example 2: Standalone Inventory Tracking App
{
'name': 'Inventory Optimization Suite',
'version': '18.0.1.0.0',
'category': 'Inventory',
'summary': 'Track reorder cycles and optimize inventory levels',
'author': 'Your D2C Brand',
'website': 'https://yourshop.com',
'license': 'AGPL-3',
'depends': ['inventory', 'mail', 'stock'],
'data': [
'security/ir.model.access.csv',
'views/low_stock_alert_views.xml',
'views/reorder_tracking_views.xml',
'reports/inventory_analytics_report.xml',
'data/default_categories.xml',
],
'demo': [
'demo/sample_alerts.xml',
],
'external_dependencies': {
'python': ['requests'], # For API calls
},
'installable': True,
'application': True, # Standalone app
'auto_install': False,
'post_init_hook': 'post_init_setup',
}
Example 3: API Integration Module with External Dependencies
{
'name': 'Multi-Channel Sales Sync',
'version': '18.0.1.0.0',
'category': 'eCommerce',
'summary': 'Sync Odoo orders with Shopify and Amazon',
'author': 'Your D2C Brand',
'license': 'AGPL-3',
'depends': ['sale', 'stock', 'account'],
'data': [
'security/ir.model.access.csv',
'views/channel_config_views.xml',
'views/sync_log_views.xml',
'data/cron_jobs.xml',
],
'external_dependencies': {
'python': ['shopify', 'boto3', 'requests'], # Third-party APIs
},
'installable': True,
'application': True,
'pre_init_hook': 'pre_init_check_dependencies',
'post_init_hook': 'post_init_setup',
'uninstall_hook': 'uninstall_cleanup',
}
Action Items: Build Proper __manifest__.py
For New Module Development
❏ Create __manifest__.py with all required fields (name, version, license)
❏ List ALL dependencies in depends field (don't skip this!)
❏ Order data files correctly (data before views)
❏ Add external_dependencies if using Python packages
❏ Set application=True for standalone apps, False for extensions
❏ Use hooks (pre_init, post_init) if setup needed
For Existing Modules
❏ Review manifest for missing dependencies
❏ Verify version matches Odoo version (18.0.x.x.x for Odoo 18)
❏ Check license is open source (AGPL-3 recommended)
❏ Ensure all data files are listed in correct order
Before Publishing
❏ Test module installation on clean Odoo instance
❏ Verify all dependencies install correctly
❏ Check that hooks run without errors
❏ Document any external requirements
Frequently Asked Questions
What happens if I forget to list a dependency in the depends field?
Your module will try to load before the dependency. If your code inherits from or references models in that dependency, you'll get a "Model not found" error and installation fails. Example: Code inherits from purchase.order but manifest doesn't list 'purchase' in depends. Odoo loads your module before purchase module. Inheritance breaks. Always list ALL dependencies in the correct order.
Why does the order of data files in the data field matter?
Files are loaded sequentially in the order listed. If a view references default categories, but categories don't exist yet (loaded after view), view loading fails. Rule: Load data files BEFORE view files. Order: (1) security rules, (2) default data, (3) views, (4) reports. Views often reference data records—data must exist first.
What's the difference between pre_init_hook and post_init_hook?
pre_init_hook: Runs BEFORE module installation. Use for: checking prerequisites (Python packages installed?), creating custom database tables, verifying system compatibility, data migration prep. If returns False, installation cancelled. post_init_hook: Runs AFTER module installation. Use for: creating default records, sending admin notifications, setting up workflows, auto-installing related modules. Module already installed when this runs.
Do external_dependencies get auto-installed by Odoo?
No. Odoo only CHECKS if they exist. If you list 'python': ['requests'], Odoo verifies requests is installed. If not, shows error message telling user to install it manually via pip install requests. Odoo does NOT auto-install. User must install dependencies themselves. Workaround: Use pre_init_hook to check and install automatically (advanced, not recommended for public modules).
Free Module Development Review
Stop guessing if your __manifest__.py is correct. We'll audit your __manifest__.py for missing fields and gotchas, verify dependency order and completeness, check version numbering convention, review hooks and installation logic, and test module installation and uninstallation. Most D2C brands discover their __manifest__.py has subtle bugs causing installation headaches.
