Mastering Function Overrides in Odoo 17
One of Odoo’s most powerful features is its modularity. You never modify the core source code directly. Instead, you use Python inheritance to "hook" into existing methods, allowing you to execute your custom logic before, after, or instead of the standard behavior.
This guide explores the three primary patterns for overriding functions in Odoo 17 models.
The Prerequisites
To override a function, you must have a custom module set up. Your Python file must import the necessary Odoo components and inherit the model you wish to modify using the _inherit attribute.
from odoo import models, api, fields
class SaleOrder(models.Model):
_inherit = 'sale.order'
Pattern 1: Extending Logic (The super() Call)
This is the most common and safest pattern. You want the standard Odoo code to run, but you want to add your own steps either before or after that code executes. We use Python's built-in super() function to trigger the parent class's implementation.
Example: Sending a notification after confirming a Sale Order
We want the standard action_confirm to run (changing the state to 'sale', creating pickings, etc.), and then log a customized internal note.
class SaleOrder(models.Model):
_inherit = 'sale.order'
def action_confirm(self):
# 1. PRE-LOGIC: You can add checks here before Odoo does its work.
print("About to confirm order:", self.name)# 2. SUPER CALL: Execute the standard Odoo behavior
res = super(SaleOrder, self).action_confirm()
# 3. POST-LOGIC: The order is now confirmed. Add custom code here.
for order in self:
order.message_post(body="Order confirmed successfully via Custom Module!")
# 4. RETURN: Always return the result of the super call
return res
Pattern 2: Intercepting Arguments (The create and write Methods)
This pattern is used for modifying data before it is saved to the database. You override the create (for new records) or write (for updates) methods and manipulate the vals dictionary.
Example: Auto-generating a reference on creation
We will modify the vals dictionary to sanitize an email before the record is created.
class ResPartner(models.Model):
_inherit = 'res.partner'
@api.model
def create(self, vals):
# Modify the arguments BEFORE calling super
if 'email' in vals and vals['email']:
vals['email'] = vals['email'].strip().lower() # Sanitize email
# Add a default generic note if none exists
if 'comment' not in vals:
vals['comment'] = "Created via API/Custom Interface"
# Call super to actually create the record in the DB
record = super(ResPartner, self).create(vals)
return record
Pattern 3: Complete Replacement (The "No Super" Approach)
In rare cases, the standard Odoo logic must be completely ignored. You redefine the method without calling super().
⚠️ Warning: This is risky. If Odoo updates the base module and changes critical logic in that function, your override will silently ignore those updates, potentially breaking core functionality. Use this only when absolutely necessary.
Example: Overriding a computation
Completely replacing how a discount is calculated, ignoring Odoo's default logic.
class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'
def _compute_discount(self):
# We do NOT call super() here
for line in self:
# Custom logic entirely replacing standard Odoo logic
if line.product_id.is_vip_product:
line.discount = 50.0
else:
line.discount = 0.0
🚀 Closing Thoughts: Mastering Odoo Customization
Successfully modifying existing Odoo functions relies entirely on Python inheritance and the strategic use of the super() function. By prioritizing methods that extend (add logic before/after the original code) and intercept (modify data before saving), you ensure compatibility and maintainability. Only in rare, critical circumstances should you choose to replace functionality entirely by omitting the super() call. Embracing this modular approach guarantees that your customizations are robust, clean, and positioned for seamless integration with future Odoo upgrades.
