Mastering Monkey Patching in Odoo 18: Modifying Core Utilities
By Braincuber Team
Published on February 11, 2026
In Odoo, the golden rule of customization is Inheritance (_inherit). It is safe, clean, and framework-compliant. But what happens when you need to modify a standalone utility function, a third-party library method, or a core class that doesn't respect the Odoo model registry?
Enter Monkey Patching. It is the practice of dynamically altering code at runtime. While powerful, it is dangerous if misused. In this tutorial, we will explore when to use it and implement a safe, robust monkey patch to modify Odoo's core PDF generation utility without breaking the system.
Inheritance vs. Monkey Patching
- Inheritance (Preferred): Uses
_inherit. Works for Odoo Models, Controllers, and Views. Preserves thesuper()chain. - Monkey Patching: Direct reassignment (
Module.func = new_func). Works for standalone functions, standard libraries, and core tools. Does not have a built-insuper()mechanism (you must implement it manually).
The Scenario: Patching a Private Utility
Imagine you need every PDF generated by Odoo (Invoices, Reports) to have a custom metadata tag for tracking. Odoo uses odoo.tools.pdf.merge_pdf to combine report pages. This is a standalone function, not a Model method, so _inherit wont work.
Step 1: Implementing the Patch
Create a new python file in your module, e.g., models/ir_actions_report_patch.py. Note that you don't even need to define a class if you are just patching a tool, but it's good practice to ensure the code loads appropriately.
import logging
from odoo.tools import pdf
_logger = logging.getLogger(__name__)
# 1. Capture the original function reference
# This acts as our "super()"
original_merge_pdf = pdf.merge_pdf
# 2. Define the new wrapper function
def unique_merge_pdf(pdf_data_list):
_logger.info(f"Intercepted PDF Merge! Merging {len(pdf_data_list)} documents.")
# 3. Add custom logic BEFORE the original
# (Example: We could validate or modify the input list here)
if not pdf_data_list:
_logger.warning("Empty PDF list provided to merge_pdf")
# 4. Call the original function
result = original_merge_pdf(pdf_data_list)
# 5. Add custom logic AFTER the original
# (Example: You could inspect the result size)
return result
# 6. Apply the Patch
# We only want to apply this once to avoid recursion loops
if pdf.merge_pdf != unique_merge_pdf:
pdf.merge_pdf = unique_merge_pdf
_logger.info("Monkey Patch applied to odoo.tools.pdf.merge_pdf")
Risks & Best Practices
- Idempotency: Always check if the patch is already applied (
if module.func != my_func). If you reload modules (e.g., during development), a blind assignment can lead toRecursionErrorwhere the function wraps itself repeatedly. - Stability: Monkey patching breaks upgrades. If Odoo changes the signature of
merge_pdfin Odoo 19, your patch will crash the server. Document your patches heavily. - Scope: Apply patches at the top level of your module's Python files (e.g., in
__init__.pyor a dedicated file imported by it) to ensure they run on server startup.
Conclusion
Monkey patching is a precision scalpel. Use it for core utilities, third-party libraries, or temporary hotfixes. For everything else in Odoo, stick to _inherit.
Facing Core Odoo Limitations?
Sometimes inheritance just isn't enough. Our expert Odoo developers can help you navigate complex architectural challenges safely.
