How to Use Hooks for Data Patching in Odoo 19: Complete Guide
In Odoo development, data often needs to be updated when modules are installed or modified. Keeping existing records consistent is especially important in production databases that contain large amounts of data. Manual updates can become difficult and time-consuming, which is why automated data patching is commonly used. This is where hooks for data patching become useful. Hooks allow developers to execute custom logic automatically during a module's lifecycle, making it easier to update existing records efficiently and reliably. This complete step by step technical guide walks through using pre_init_hook, post_init_hook, and uninstall_hook with real Python code examples.
What You'll Learn:
- What data patching is and when to use it in Odoo development
- How to use pre_init_hook to prepare database schema before module installation
- How to use post_init_hook to update existing records after module installation
- How to use uninstall_hook to clean up configuration data on module removal
- How to register hooks in the manifest file and structure your module
What Is Data Patching?
Data patching refers to the process of updating or modifying existing records in the database programmatically. Instead of manually editing records through the user interface or writing raw SQL queries, developers can use Python code to apply updates in a controlled and automated way.
Data patching is especially useful when new fields are added and need default values, existing data contains incorrect or outdated values, system configurations need to be updated, or data needs to be aligned with new business logic.
Why Use Hooks for Data Patching?
Hooks are designed to execute automatically at specific stages of a module's lifecycle, such as during installation or upgrade. This makes them highly suitable for applying data patches at the exact moment when changes are required. Instead of relying on manual updates or external scripts, hooks allow developers to embed data correction logic directly into the module. This ensures that whenever the module is installed or updated, the required data changes are applied consistently across the system.
Automation
Data updates are executed automatically without requiring manual intervention, reducing operational effort and ensuring timely updates across all environments.
Consistency
Ensures all relevant records are updated uniformly, avoiding partial or inconsistent data states across the database and preventing data integrity issues.
Efficiency
Capable of handling large volumes of data through optimized ORM operations with batch processing, minimising memory usage and server load.
Reliability
Minimises the risk of human error and ensures repeatable, predictable outcomes with every module installation or upgrade across environments.
Hooks Used for Data Patching
Odoo provides different types of hooks that execute at specific stages of a module lifecycle. In Odoo 19, all hooks receive env directly as a parameter, giving full access to the ORM and database cursor.
| Hook | Timing | Use Case |
|---|---|---|
| pre_init_hook | Before module installation | Create database columns using SQL before ORM loads |
| post_init_hook | After module installation | Update records, set defaults, configure system parameters |
| uninstall_hook | On module removal | Clean up config data and remove module-created records |
Setting Up the Model
Before writing any hooks, the first step is to define the field that the hooks will work with. In this example, we are adding a priority field to the existing res.partner model in Odoo. The res.partner model is the built-in model for all contacts including customers, vendors, and companies. We extend it using _inherit, which lets us add new fields and logic without touching Odoo's core code.
The priority field is what the hooks will operate on. The pre_init_hook creates its database column before installation, and the post_init_hook assigns default values to all existing partners after installation.
from . import res_partner
from odoo import fields, models
class ResPartner(models.Model):
_inherit = 'res.partner'
priority = fields.Selection(
selection=[
('low', 'Low'),
('normal', 'Normal'),
('high', 'High'),
],
string='Priority',
default='normal',
index=True,
help='Set the priority level for this partner.'
)
res.partner.tree.priority
res.partner
99
res.partner.form.priority
res.partner
99
Using pre_init_hook
This hook runs before the module is installed. It checks if the priority column already exists in the res_partner table. If it does not exist, it creates the column directly using SQL to prepare the database safely before the ORM loads.
from . import models
from odoo.tools.sql import column_exists, create_column
def pre_init_hook(env):
if not column_exists(env.cr, "res_partner", "priority"):
create_column(env.cr, "res_partner", "priority", "varchar")
What happens when the module installs: Odoo checks the res_partner table in the database. If the priority column does not exist, it is created immediately using SQL before the ORM tries to load the model. This prevents any migration errors on existing databases.
Odoo 19 Note
In Odoo 19, pre_init_hook receives env directly as a parameter. The ORM is fully available inside all hooks — use env['model'] for ORM operations or env.cr for direct SQL operations.
Using post_init_hook
This hook runs after the module is fully installed. It finds all existing partner records that have no priority set and automatically assigns them a default value of normal. This ensures all existing data is consistent from day one.
def post_init_hook(env):
partners = env['res.partner'].search([('priority', '=', False)])
batch_size = 100
for i in range(0, len(partners), batch_size):
batch = partners[i:i + batch_size]
batch.write({'priority': 'normal'})
env['ir.config_parameter'].sudo().set_param(
'partner_priority_update.default_priority',
'normal'
)
Batch Processing
Always use batch processing for large datasets to avoid memory issues and server timeouts. The example above processes partners in batches of 100 records at a time, which is a safe default for most production environments.
{
'name': 'Partner Priority Update',
'version': '19.0.1.0.0',
'category': 'Technical',
'summary': 'Manage and classify partners by priority level',
'author': 'Your Company',
'website': 'https://www.yourcompany.com',
'license': 'LGPL-3',
'depends': ['base'],
'data': [
'views/res_partner_views.xml',
],
'pre_init_hook': 'pre_init_hook',
'post_init_hook': 'post_init_hook',
'installable': True,
'auto_install': False,
'application': False,
}
What happens when the module installs: Odoo searches for all partner records where priority is empty. It updates them in batches of 100 at a time, writing normal as the priority value. Once complete, every existing partner in the system has a priority value without any manual work. It also saves a system parameter with the key partner_priority_update.default_priority so the uninstall_hook can find and remove it later.
Using uninstall_hook
This hook runs when the module is uninstalled. It removes the configuration data that was created during installation so no leftover data remains in the database after the module is removed.
def uninstall_hook(env):
param = env['ir.config_parameter'].sudo().search([
('key', '=', 'partner_priority_update.default_priority')
])
if param:
param.unlink()
{
'name': 'Partner Priority Update',
'version': '19.0.1.0.0',
'category': 'Technical',
'summary': 'Manage and classify partners by priority level',
'author': 'Your Company',
'website': 'https://www.yourcompany.com',
'license': 'LGPL-3',
'depends': ['base'],
'data': [
'views/res_partner_views.xml',
],
'pre_init_hook': 'pre_init_hook',
'post_init_hook': 'post_init_hook',
'uninstall_hook': 'uninstall_hook',
'installable': True,
'auto_install': False,
'application': False,
}
What happens when the module uninstalls: Odoo searches for the config parameter with the key partner_priority_update.default_priority that was stored during installation. If it finds it, it deletes it. This keeps the database clean with no leftover data after the module is removed.
You can verify this by going to Settings > Technical > Parameters > System Parameters and searching for partner_priority_update. It will be visible before uninstall and gone after.
When Not to Use Hooks
When to Avoid Hooks
- Avoid using hooks for extremely large datasets without proper batching, as it may impact performance during installation
- Do not use hooks for logic that needs to run on every module upgrade; use migration scripts instead
- Avoid placing heavy or time-consuming operations inside hooks, as they can slow down module installation
Using hooks for data patching in Odoo 19 provides a powerful and automated way to maintain data consistency across modules. By leveraging hooks like pre_init_hook, post_init_hook, and uninstall_hook, developers can ensure that existing records, configurations, and system data are properly updated during installation without any manual effort. This approach not only improves efficiency but also guarantees that every deployment remains consistent and reliable. When used correctly with proper checks and optimized logic, hooks help build robust Odoo modules that are easier to maintain, scale, and deploy.
Frequently Asked Questions
When should I use pre_init_hook instead of post_init_hook?
Use pre_init_hook when you need to prepare the database schema before the module is installed, such as creating columns using SQL before the ORM loads the model. Use post_init_hook when you want to update records, create data, or configure the system after installation using the ORM.
Does post_init_hook run on module upgrade?
No, the post_init_hook is executed only once during the initial installation of the module. It will not run again when the module is upgraded. For upgrade-specific logic, use migration scripts instead.
Is it safe to use the ORM inside Odoo hooks?
Yes, the ORM is fully safe to use in all three hooks — pre_init_hook, post_init_hook, and uninstall_hook — because all of them receive env directly as a parameter in Odoo 19, giving full access to model methods and the database cursor.
What happens if a hook fails during module installation?
If a hook raises an exception, the entire module installation process will fail and the transaction will be rolled back. This ensures that no partial or inconsistent data is saved in the database. Always test hooks thoroughly before deploying to production.
Can I use hooks to update existing records across multiple models?
Yes, hooks can interact with any model available in the Odoo registry. You can search, create, write, and unlink records across multiple models within a single hook function, making them suitable for complex data migration and patching scenarios.
Need Help with Odoo Development?
Our Odoo development experts can help you build custom modules with hooks for data patching, configure module lifecycle automation, implement batch processing for large datasets, and optimise your entire Odoo development workflow.
About the author
Founder & Odoo Practice Lead, Braincuber Technologies
Founder of Braincuber. Has scoped and shipped 500+ Odoo implementations for US mid-market and global brands. Takes every founder call personally — no SDR layer between buyers and the people building the system.
