Skip to content

Unlock Odoo Model Inheritance: A Comprehensive 3-Step Tutorial for Powerful Customization

keyphrase odoo model inheritance

Elevate Your Odoo Development: Mastering Odoo Model Inheritance

Odoo Model Inheritance is a cornerstone of robust Odoo development, enabling developers to build highly flexible, scalable, and maintainable applications. Whether you’re customizing existing Odoo modules or developing new ones from scratch, a deep understanding of how models inherit, extend, and delegate functionality is absolutely essential. This comprehensive tutorial will guide you through the three primary patterns of Odoo Model Inheritance: Classical Inheritance, Extension, and Delegation. By the end, you’ll be equipped to choose the right strategy for any Odoo development challenge, ensuring your solutions are elegant and efficient.

We’ll explore each type with clear explanations, practical code examples, and step-by-step implementation instructions for Odoo 16/17/18/19 environments. Let’s dive in and unlock the full potential of your Odoo projects!


1. Classical Inheritance: Specializing Your Models

Classical Inheritance in Odoo is the fundamental object-oriented programming concept where a new model (the child or sub-model) derives properties and behaviors from an existing model (the parent or base model). Think of it as creating a more specialized version of a general concept. In Odoo’s ORM (Object-Relational Mapping) context, this means the child model inherits the table schema, fields, and methods from its parent. This is a powerful way to promote code reuse and establish clear hierarchical relationships between your data structures.

Why and When to Use Classical Inheritance?

You should consider Classical Inheritance when:

  • You need to create a specialized version of an existing custom model. For instance, if you have a Vehicle model and want to create Car and Motorcycle models that share common vehicle attributes but also have their own unique characteristics.
  • You want to reuse a significant amount of code and data structure from a base model without modifying the base model itself.
  • You are building a complex data hierarchy where sub-models naturally fit under a broader category.

Code for Models (Inheritance0 and Inheritance1)

This code demonstrates how Inheritance1 inherits from Inheritance0 using the _inherit class attribute, effectively showcasing Odoo Model Inheritance in action.

from odoo import models, fields, api

class Inheritance0(models.Model):
    _name = 'inheritance.0'
    _description = 'Inheritance Zero - Parent Model'

    name = fields.Char(string="Name")
    common_property = fields.Char(string="Common Property", default="Default Value")

    def call(self):
        """A method to demonstrate calling inherited methods."""
        return self.check("model 0")

    def check(self, s):
        """This method is inherited by Inheritance1."""
        return "This is {} record {} with common property: {}".format(s, self.name, self.common_property)

class Inheritance1(models.Model):
    # '_inherit' links this model to 'inheritance.0'
    # 'inheritance.0' here is the technical name of the model being inherited from.
    _inherit = 'inheritance.0'
    _description = 'Inheritance One - Child Model'
    
    # 'name' and 'common_property' fields are automatically inherited.
    # We can add new fields specific to Inheritance1
    specific_field = fields.Integer(string="Specific Value", default=100)
    
    # The call() method will be overridden, but check() remains inherited.
    def call(self):
        """Overrides the parent's call method."""
        return self.check("model 1") + " (specific: {})".format(self.specific_field)

    @api.model
    def create_and_test_classical_inheritance(self):
        """Helper method to test classical inheritance."""
        self.env['inheritance.0'].search([]).unlink() # Clean up previous test records
        self.env['inheritance.1'].search([]).unlink()

        record_0 = self.env['inheritance.0'].create({'name': 'Parent Record A', 'common_property': 'Parent-specific'})
        record_1 = self.env['inheritance.1'].create({'name': 'Child Record B', 'specific_field': 200})

        print(f"Parent Record Call: {record_0.call()}")
        print(f"Child Record Call: {record_1.call()}")
        print(f"Child Record Inherited Name: {record_1.name}")
        print(f"Child Record Specific Field: {record_1.specific_field}")
        
        # Expected Output:
        # Parent Record Call: This is model 0 record Parent Record A with common property: Parent-specific
        # Child Record Call: This is model 1 record Child Record B with common property: Default Value (specific: 200)
        # Child Record Inherited Name: Child Record B
        # Child Record Specific Field: 200

Steps to Implement in Odoo 16/17/18/19:

  1. Create a New Module: Start by creating a new Odoo module (e.g., my_inheritance_module). Ensure you have the standard module structure with __manifest__.py, models/, views/, etc.
  2. Define Your Parent and Child Models: Inside your models directory, create a Python file (e.g., classical_inheritance.py). Paste the Inheritance0 and Inheritance1 code provided above into this file. Remember to include from odoo import models, fields, api at the top.
  3. Update __init__.py: In your models/__init__.py file, ensure you import your new model file: from . import classical_inheritance.
  4. Configure __manifest__.py: Update your module’s __manifest__.py file to include the new Python file and set base as a dependency:
{
    'name': 'My Inheritance Module',
    'version': '1.0',
    'summary': 'Demonstrates Odoo Model Inheritance patterns',
    'description': """
    This module showcases Classical Inheritance, Extension, and Delegation in Odoo.
    """,
    'author': 'Your Name',
    'website': 'https://www.yourwebsite.com',
    'category': 'Technical',
    'depends': ['base'],
    'data': [
        # 'security/ir.model.access.csv', # If you add specific access rights
        # 'views/inheritance_views.xml',  # If you create UI for these models
    ],
    'application': False,
    'installable': True,
    'auto_install': False,
    'license': 'LGPL-3',
}
  1. Install/Upgrade the Module: In your Odoo instance, go to Apps, update your app list, and then install (or upgrade if it’s an existing module) My Inheritance Module.
  2. Test the Inheritance: You can test the interaction by running the create_and_test_classical_inheritance method from the Odoo shell or by creating records in the UI if you’ve added views. The output demonstrates how Inheritance1 reuses Inheritance0‘s structure and methods while allowing for specialization.

Explanation of Classical Inheritance

  • Inheritance0 defines the base structure with name and common_property fields, plus call() and check() methods.
  • _inherit = 'inheritance.0': This critical line in Inheritance1 tells Odoo to create a new, distinct database table for Inheritance1, but to essentially copy the schema and methods from inheritance.0. Inheritance1 is a sub-class of Inheritance0.
  • Inheritance1 automatically gains access to name, common_property, and check().
  • It then overrides call() to provide its own behavior, demonstrating method overriding while still leveraging the inherited check() method. Notice how Inheritance1‘s records will have their own id and _name (inheritance.1), separate from inheritance.0. This is the hallmark of true sub-classing.

2. Extension: Augmenting Existing Models

youtube_link

Extension, also known as modular inheritance or “prototype inheritance” in Odoo, is a highly frequent and practical form of Odoo Model Inheritance. Its purpose is to add new fields or methods to an existing model, rather than creating a brand new, separate model. This is crucial for customizing Odoo’s core functionalities (like res.partner, product.template, sale.order) without directly modifying their source code, ensuring cleaner upgrades and module compatibility.

Why and When to Use Extension?

Use Extension when:

  • You need to add custom fields to an Odoo core model (e.g., adding a customer_tier field to res.partner).
  • You want to override or add new methods to an existing Odoo model’s logic.
  • You are building a custom module that integrates closely with standard Odoo functionalities and requires modifications to their data or behavior.
  • You aim to keep your customizations separate from Odoo’s core, facilitating upgrades.

Code for Models (Extension0 and Extension1)

Model Extension1 extends model Extension0 by adding a new field, showcasing how Odoo Model Inheritance can modify existing structures.

from odoo import models, fields, api

class Extension0(models.Model):
    _name = 'extension.0'
    _description = 'Extension Zero - Base Model'

    name = fields.Char(string="Name", default="Base Name")

class Extension1(models.Model):
    # _inherit and _name are the same as the model being extended.
    # This is the key to extending an existing model's table schema.
    _inherit = 'extension.0'
    _name = 'extension.0' # Explicitly stating _name is 'extension.0' confirms the extension
    _description = 'Extension One - Extends Base Model'
    
    # Adding a new field to the existing 'extension.0' model's table
    extended_description = fields.Char(string="Extended Description", default="Default Extended")
    priority = fields.Selection([('low', 'Low'), ('medium', 'Medium'), ('high', 'High')], 
                                string="Priority", default="medium")

    @api.model
    def create_and_test_extension(self):
        """Helper method to test model extension."""
        self.env['extension.0'].search([]).unlink() # Clean up previous test records
        
        record = self.env['extension.0'].create({'name': 'My Extended Item'})

        print(f"Record Name: {record.name}")
        print(f"Record Extended Description: {record.extended_description}")
        print(f"Record Priority: {record.priority}")
        
        # Expected Output:
        # Record Name: My Extended Item
        # Record Extended Description: Default Extended
        # Record Priority: medium

Steps to Implement in Odoo 16/17/18/19:

  1. Use an Existing Module or Create a New One: You can add this code to your my_inheritance_module or create a new module (e.g., my_extension_module).
  2. Define Your Extension Model: Inside your models directory, create a Python file (e.g., extension.py). Paste the Extension0 and Extension1 code.
  3. Update __init__.py: Ensure your models/__init__.py file imports the new file: from . import extension.
  4. Configure __manifest__.py: If you created a new module, set up its __manifest__.py similar to the Classical Inheritance example. If adding to my_inheritance_module, ensure your models/extension.py is included if you modified the data list.
  5. Install/Upgrade the Module: Go to Apps, update your app list, and then install or upgrade your module.
  6. Test the Extension: Use the create_and_test_extension method from the Odoo shell. You’ll observe that creating a record in extension.0 (the base model) now automatically includes the fields defined in Extension1 (extended_description, priority).

Explanation of Extension

  • Extension0 is the base model, defining a name field.
  • _inherit = 'extension.0' AND _name = 'extension.0': This combination is the hallmark of extension. It tells Odoo: “Don’t create a new table. Instead, modify the existing table that corresponds to the extension.0 model.”
  • The extended_description and priority fields are then physically added to the database table of extension.0. Any existing or new records of extension.0 will automatically have these new fields.
  • This approach seamlessly integrates your custom additions into the target model, making it appear as if the fields were always part of the original definition. It’s the standard and recommended way to customize core Odoo models.

3. Delegation: Composing Models for Clean Data Structure

Delegation, enabled by the _inherits attribute, represents a powerful form of Odoo Model Inheritance focused on composition rather than strict inheritance. It allows one model to “delegate” the management of certain fields to another model. From the user’s perspective, these delegated fields appear to belong to the main model, but internally, Odoo manages a Many2One relationship to a separate record in the delegated model. This promotes clean data separation and modularity, especially for complex entities.

Why and When to Use Delegation?

Choose Delegation when:

  • You have a complex object that can be naturally broken down into smaller, self-contained components. For example, a Laptop can delegate its Screen and Keyboard properties to separate models.
  • You want to promote a “composition over inheritance” approach, leading to more flexible and maintainable code.
  • You need to reuse common components across different models without duplicating their fields.
  • The delegated components might have their own lifecycle or be shared by other models (though typically in _inherits, the delegated record is specific to the delegating record).

Code for Models (Screen, Keyboard, and Laptop)

The Laptop model delegates its display and input-related fields to Screen and Keyboard models, a prime example of effective Odoo Model Inheritance for complex objects.

from odoo import models, fields, api

class Screen(models.Model):
    _name = 'delegation.screen'
    _description = 'Screen Component'

    size = fields.Float(string='Screen Size (Inches)', help="Diagonal size of the screen.")
    resolution = fields.Char(string='Resolution', default='1920x1080')

class Keyboard(models.Model):
    _name = 'delegation.keyboard'
    _description = 'Keyboard Component'

    layout = fields.Char(string='Layout', help="e.g., QWERTY, AZERTY")
    backlit = fields.Boolean(string='Backlit', default=False)

class Laptop(models.Model):
    _name = 'delegation.laptop'
    _description = 'Laptop Device'

    # Delegation configuration
    # Maps delegated model's technical name to the Many2One field linking it.
    _inherits = {
        'delegation.screen': 'screen_id',
        'delegation.keyboard': 'keyboard_id',
    }

    name = fields.Char(string='Laptop Name', required=True)
    maker = fields.Char(string='Manufacturer')
    processor = fields.Char(string='Processor', default='Intel Core i5')

    # Many2One fields that explicitly connect Laptop to Screen and Keyboard.
    # These are crucial for delegation management.
    screen_id = fields.Many2one(
        'delegation.screen', 
        string='Screen Details',
        required=True, 
        ondelete='cascade', # Important: Deletes screen record when laptop is deleted
        help="Links to the screen specifications for this laptop."
    )
    keyboard_id = fields.Many2one(
        'delegation.keyboard', 
        string='Keyboard Details',
        required=True, 
        ondelete='cascade', # Important: Deletes keyboard record when laptop is deleted
        help="Links to the keyboard specifications for this laptop."
    )

    @api.model
    def create_and_test_delegation(self):
        """Helper method to test model delegation."""
        self.env['delegation.laptop'].search([]).unlink() # Clean up previous test records
        self.env['delegation.screen'].search([]).unlink()
        self.env['delegation.keyboard'].search([]).unlink()

        # Create delegated records first
        screen_record = self.env['delegation.screen'].create({'size': 15.6, 'resolution': '2560x1440'})
        keyboard_record = self.env['delegation.keyboard'].create({'layout': 'QWERTY', 'backlit': True})

        # Create the Laptop record, linking to the delegated components
        laptop_record = self.env['delegation.laptop'].create({
            'name': 'ProBook X1',
            'maker': 'TechCorp',
            'processor': 'AMD Ryzen 7',
            'screen_id': screen_record.id,
            'keyboard_id': keyboard_record.id,
        })

        print(f"Laptop Name: {laptop_record.name}")
        print(f"Laptop Maker: {laptop_record.maker}")
        print(f"Laptop Processor: {laptop_record.processor}")
        # Accessing delegated fields directly:
        print(f"Screen Size: {laptop_record.size} inches")
        print(f"Screen Resolution: {laptop_record.resolution}")
        print(f"Keyboard Layout: {laptop_record.layout}")
        print(f"Keyboard Backlit: {laptop_record.backlit}")
        
        # Expected Output:
        # Laptop Name: ProBook X1
        # Laptop Maker: TechCorp
        # Laptop Processor: AMD Ryzen 7
        # Screen Size: 15.6 inches
        # Screen Resolution: 2560x1440
        # Keyboard Layout: QWERTY
        # Keyboard Backlit: True

Steps to Implement in Odoo 16/17/18/19:

  1. Use an Existing Module or Create a New One: Add this code to my_inheritance_module or a new module (e.g., my_delegation_module).
  2. Define Your Delegated and Delegating Models: Inside your models directory, create a Python file (e.g., delegation.py). Paste the Screen, Keyboard, and Laptop model code.
  3. Update __init__.py: Ensure your models/__init__.py file imports the new file: from . import delegation.
  4. Configure __manifest__.py: Update your module’s __manifest__.py if necessary.
  5. Install/Upgrade the Module: Install or upgrade your module.
  6. Test the Delegation: Run the create_and_test_delegation method from the Odoo shell. Observe how you can access size, resolution, layout, and backlit directly through the laptop_record object, even though they belong to separate Screen and Keyboard records.

Explanation of Delegation

  • _inherits: This is a dictionary where keys are the technical names of the models being delegated to ('delegation.screen', 'delegation.keyboard'), and values are the names of the Many2one fields ('screen_id', 'keyboard_id') in the Laptop model that link to these delegated records.
  • screen_id and keyboard_id: These are standard Many2one fields. When a Laptop record is created, new Screen and Keyboard records are often created and linked via these Many2one fields.
  • Direct Field Access: The magic of _inherits is that it allows laptop_record.size to directly fetch the size from the linked Screen record, and laptop_record.layout to fetch the layout from the linked Keyboard record. Odoo’s ORM handles the underlying data redirection seamlessly.
  • ondelete='cascade': This is a critical option for delegated Many2one fields. It ensures that when a Laptop record is deleted, its associated Screen and Keyboard records are also deleted, preventing orphaned data in your database.
  • Delegation offers a clean way to model “has-a” relationships (e.g., a Laptop has a Screen) while providing convenient access to sub-component properties.

Best Practices and Choosing the Right Odoo Model Inheritance Approach

Understanding the nuances of each Odoo Model Inheritance pattern is key to building maintainable and scalable Odoo applications. Here’s a quick guide and some best practices:

  1. Classical Inheritance (_inherit creates a new model/table):
    • When to use: To create specialized versions of your own custom base models.
    • Benefits: Strong type hierarchy, code reuse, clear parent-child relationship.
    • Caution: Avoid using it to customize Odoo core models directly, as it creates a separate table and won’t merge fields with the original. It can also lead to deep hierarchies that are hard to manage.
  2. Extension (_inherit and _name are the same):
    • When to use: The primary method for customizing and extending existing Odoo core models (e.g., res.partner, product.template) or custom models from other modules.
    • Benefits: Modifies the existing model’s table, adds fields/methods directly, ensures compatibility with Odoo upgrades, modular and clean.
    • Caution: Can lead to very large models if too many modules extend the same core model. Ensure field names are unique to avoid conflicts.
  3. Delegation (_inherits):
    • When to use: When a complex model can be logically broken down into several distinct component models, and you want to manage these components separately but access their fields directly from the main model. Ideal for “has-a” relationships.
    • Benefits: Promotes composition over inheritance, cleaner data structure, modularity, simplifies user interaction by exposing delegated fields directly.
    • Caution: Requires careful management of Many2one fields (especially ondelete='cascade') and can add a slight overhead due to underlying joins for data access.

General Best Practices:

  • Prioritize Extension for Core Models: Always use Extension when modifying standard Odoo models to ensure upgradability and module compatibility.
  • Favor Composition (Delegation) for Complex Objects: When designing your own complex data models, consider if Delegation can lead to a cleaner, more modular structure than deep Classical Inheritance.
  • Keep Models Focused: Each model should ideally represent a single concept. Don’t overload a single model with too many unrelated fields, regardless of the inheritance type.
  • Document Your Design: Clearly document your inheritance choices within your code and module descriptions. Future developers (and your future self!) will thank you.
  • Test Thoroughly: Always write tests for your inherited, extended, or delegated models to ensure they behave as expected.

By mastering these three forms of Odoo Model Inheritance, you gain incredible power and flexibility in Odoo development. This knowledge empowers you to build robust, maintainable, and highly customized Odoo solutions that can adapt and grow with your business needs. Happy coding!


External Resources for Further Learning:

Internal Links (Placeholder):


Discover more from teguhteja.id

Subscribe to get the latest posts sent to your email.

Leave a Reply

WP Twitter Auto Publish Powered By : XYZScripts.com