In Odoo, hooks are designated Python functions that allow developers to execute custom code at very specific stages of a module's lifecycle. While standard Odoo development relies on XML records and Python ORM methods to build functionality, hooks give you low-level access to the environment precisely when a module is being installed, loaded, or removed.
The Purpose of Hooks
The primary purpose of these hooks is to bridge scenarios that standard Odoo data files (XML/CSV) or basic model operations cannot handle gracefully. They are used to:
- Pre-validate environments before allowing an installation to proceed.
- Seed dynamic data or configurations immediately after installation.
- Clean up external dependencies or custom database footprints upon uninstallation.
- Override or monkey-patch core Python methods right as the server loads the module imports.
The 4 Types of Hooks in Odoo
Odoo recognizes four primary hooks, each corresponding to a different lifecycle event. They are declared in your module's __manifest__.py file:
- pre_init_hook: Executes before the module is installed.
- post_init_hook: Executes immediately after the module is installed.
- uninstall_hook: Executes when the module is being uninstalled.
- post_load: Executes every time the Odoo server starts and loads the module (often used for monkey patching).
1. pre_init_hook (Before Installation)
Purpose: To perform safety checks, validate environmental dependencies, or clean up conflicting data in the database before Odoo attempts to install the module. If this hook raises an error, the installation is aborted.
Step-by-Step Example: Checking for a Python Library
Imagine your module requires the external Python library passlib to work securely. You want to stop the installation if the server doesn't have it installed.
Step 1: __init__.py — Define the logic that validates the environment:
from odoo.exceptions import UserError
def check_dependencies(cr):
"""Ensure required Python libraries exist before installing."""
try:
import passlib
except ImportError:
raise UserError(
"Installation Aborted: The 'passlib' Python library is required."
" Please install it using 'pip install passlib'."
)
Step 2: __manifest__.py — Register the hook:
{
'name': 'Secure Password Manager',
'depends': ['base'],
'pre_init_hook': 'check_dependencies',
}2. post_init_hook (After Installation)
Purpose: To populate the database with records, generate default configurations, or set up dynamic data that cannot easily be declared in static XML files.
Step-by-Step Example: Creating a Default Tag
Automatically create a "High Priority" tag so users have it available immediately upon installing a custom helpdesk module.
Step 1: __init__.py — Define the seeding logic:
def seed_default_tag(env):
"""Generate a default ticket tag after installation."""
TagModel = env['helpdesk.ticket.tag']
existing_tag = TagModel.search([('name', '=', 'High Priority')], limit=1)
if not existing_tag:
TagModel.create({
'name': 'High Priority',
'color': 2, # Standard Odoo color index for Red
})
Step 2: __manifest__.py — Register the hook:
{
'name': 'Custom Helpdesk',
'depends': ['base'],
'post_init_hook': 'seed_default_tag',
}3. uninstall_hook (During Uninstallation)
Purpose: To perform cleanup operations. While Odoo automatically removes models and fields defined in your module, it won't automatically delete external files you created, external API webhooks you registered, or scattered system parameters you injected.
Step-by-Step Example: Cleaning up System Parameters
Assume your module created an ir.config_parameter that might interfere with core Odoo behavior if left behind after uninstallation.
Step 1: __init__.py — Define the cleanup logic:
def cleanup_module_data(cr, registry):
"""Remove lingering system parameters on uninstall."""
from odoo import api, SUPERUSER_ID
env = api.Environment(cr, SUPERUSER_ID, {})
param_to_delete = env['ir.config_parameter'].sudo().search([
('key', '=', 'custom_helpdesk.api_token')
])
if param_to_delete:
param_to_delete.unlink()
Step 2: __manifest__.py — Register the hook:
{
'name': 'Custom Helpdesk API Integration',
'depends': ['base'],
'uninstall_hook': 'cleanup_module_data',
}4. post_load (Upon Server Load / Import)
Purpose: To modify or “monkey-patch” standard behavior of Odoo base classes or Python libraries before the models are fully initialized in the registry. Unlike other hooks, this runs every time the server starts, not just during module installation.
Step-by-Step Example: Patching Email Sending Behavior
Suppose you want to globally log email details every time an email is sent in Odoo, without modifying the core code.
Step 1: __init__.py — Define the logic that alters behavior upon loading:
from odoo.addons.mail.models.mail_mail import MailMail
import logging
_logger = logging.getLogger(__name__)
def custom_email_send():
if getattr(MailMail.send, "_patched", False):
return
original_send = MailMail.send
def new_send(self, auto_commit=False, raise_exception=False):
for mail in self:
_logger.info(
"Sending email | Subject: %s | To: %s",
mail.subject,
mail.email_to,
)
return original_send(
self,
auto_commit=auto_commit,
raise_exception=raise_exception,
)
new_send._patched = True
MailMail.send = new_send
Step 2: __manifest__.py — Register the post_load hook:
{
'name': 'Custom Email Logger',
'depends': ['base', 'mail'],
'post_load': 'custom_email_send',
'installable': True,
}Hooks in Odoo 19 provide a practical way to run custom logic at specific points in a module’s lifecycle, such as before installation, after setup, during uninstallation, or when the server loads. They are useful for cases where standard XML or ORM logic is not sufficient, like validating dependencies, creating dynamic data, or cleaning up configurations. When used appropriately, hooks help keep modules more reliable and easier to manage without overcomplicating the core implementation.
To read more about Overview of Owl Hooks in Odoo 18, refer to our blog Overview of Owl Hooks in Odoo 18.