Introduction
In Odoo 17, init hooks are a powerful mechanism that allows developers to execute Python logic automatically when the module is installed. They are especially useful when you need to:
- Create or modify database-level objects (SQL functions, views, triggers)
- Perform data preparation or cleanup
- Ensure system consistency outside of ORM-managed models .
Understanding Init Hooks in Odoo 17
init hook is a special method in Odoo models that gets executed once per model when the module is first installed. Odoo provides several lifecycle hooks that can be defined inside a module:
| Hook Type | When It Runs |
| post_init_hook | After module installation |
| uninstall_hook | When module is uninstalled |
| pre_init_hook | Before module installation |
These hooks are declared in the module’s manifest.py file and must point to Python functions.
Implement Init Hooks
In my case, I have a PostgreSQL function called:get_inventory_age_breakdown_data. This function is used to calculate inventory age breakdown data based on Company, Product, Category, Breakdown days and Checkpoint time
Because this logic is complex and performance-critical, it was implemented directly as a PostgreSQL function, not using Odoo ORM. That means:
- The function must exist when the module is installed
- The function must be removed when the module is uninstalled
This is a perfect use case for init hooks.
Creating the PostgreSQL Function (Post-Init Hook)
First, created a helper function to create or replace the SQL function.
def create_get_stock_age_breakdown_data_sgeede(env):
query = """
CREATE OR REPLACE FUNCTION public.get_inventory_age_breakdown_data(
company_ids INTEGER[],
product_ids INTEGER[],
category_ids INTEGER[],
breakdown_days INTEGER,
checkpoint_time TIMESTAMP
)
-- SQL logic here
"""
env.cr.execute(query)
Why CREATE OR REPLACE?
Using CREATE OR REPLACE FUNCTION ensures: -
- Safe reinstallation
- Safe module upgrade
- No error if the function already exists
Deleting the PostgreSQL Function (Uninstall Hook)
Next, created a cleanup function that drops the SQL function when the module is uninstalled.
def delete_stock_age_breakdown(env):
query = """
DROP FUNCTION public.get_inventory_age_breakdown_data(
company_ids INTEGER[],
product_ids INTEGER[],
category_ids INTEGER[],
breakdown_days INTEGER,
checkpoint_time TIMESTAMP
)
"""
env.cr.execute(query)
This ensures there are no leftover database objects after module removal.
⚠️ Important: PostgreSQL requires the function signature to match exactly when dropping a function.
Defining the Init Hook Functions
Now, wrapped those helpers into hook functions that Odoo can call.
def _sgeede_post_init(env):
create_get_stock_age_breakdown_data_sgeede(env)
def _sgeede_uninstall_init(env):
delete_stock_age_breakdown(env)
These functions are intentionally kept small and clean, delegating the real logic to helper methods.
Registering Hooks in manifest.py
To make Odoo aware of these hooks, they must be declared in the module manifest:
{
'name': 'Inventory Age Breakdown',
'version': '17.0.1.0.0',
'depends': ['stock'],
'post_init_hook': '_sgeede_post_init',
'uninstall_hook': '_sgeede_uninstall_init',
}
Once this is done:
- _sgeede_post_init runs automatically after installation
- _sgeede_uninstall_init runs automatically during uninstall
Why Init Hooks Are Important
Using init hooks gives several advantages:
✅ Clean database lifecycle management
✅ No manual SQL execution
✅ Safe upgrades and reinstallations
✅ Better maintainability for complex SQL logic
Without hooks, developers often forget to clean up database-level objects, which can cause:
- Conflicts
- Unexpected behavior
- Migration issues
Conclusion
Init hooks in Odoo 17 are essential when a module interacts directly with the database outside the ORM. In this real case, they allowed me to safely manage a PostgreSQL function used for inventory aging calculations—automatically created during installation and properly removed on uninstallation.
If you are working with SQL functions, views, or triggers, init hooks should be part of your standard development pattern. However, it is critical to understand that init hooks are designed only for one-time initialization and do not run during module updates. Knowing this limitation helps avoid outdated database logic and ensures a clean, maintainable module lifecycle.
