13 - Abstract Models (models.AbstractModel)
Welcome back! As you progress in your Odoo 19 journey, your apps are going to get bigger and more complex. Soon, you will find yourself needing the exact same fields and functions across multiple different models.
For example, what if you want every single model in your app to keep track of when a record was created and when it was last modified?
You could copy and paste creation_date and last_updated_date fields into every single Python file. But as developers, we hate repeating ourselves! This is exactly where Abstract Models come in to save the day.
1. What is an Abstract Model?
To understand an Abstract Model, let's look at what you already know:
When you use models.Model, Odoo physically creates a new table in your PostgreSQL database to hold your data.
An Abstract Model (models.AbstractModel) is completely different. It does not create a table in the database. Think of it as a blueprint, a ghost, or a template. It lives purely in your Python code. It can hold fields, compute methods, and button actions, but it just sits there waiting for a regular model to inherit it. Once a regular model inherits this "blueprint," it copies all of those features into its own real database table.
2. Writing an Abstract Model (The Blueprint)
Let’s build a highly practical example. We will create an Abstract Model that adds a "Cancellation Reason" field and a "Cancel" action. We can later attach this to any document (like a Sales Order, a Ticket, or a Leave Request).
Notice that we inherit from models.AbstractModel instead of models.Model:
from odoo import models, fields, api
class CancelReasonTemplate(models.AbstractModel):
# Abstract models still need a _name!
_name = 'cancel.reason.template'
_description = 'Template for Canceling Records'
cancel_reason = fields.Text(string="Reason for Cancellation")
is_cancelled = fields.Boolean(string="Is Cancelled?", default=False)
def action_cancel_record(self):
"""A generic method that can be used by any model that inherits this"""
for record in self:
if not record.cancel_reason:
# In a real app, you might raise a ValidationError here!
pass
record.is_cancelled = True
Because this is an Abstract Model, Odoo will not create a cancel_reason_template table in the database.
3. Inheriting the Abstract Model (The Implementation)
Now, let's say you are building a custom Helpdesk app and a custom Fleet management app. Both need this exact cancellation feature.
All you have to do is use _inherit in your regular models.Model classes!
class HelpdeskTicket(models.Model):
_name = 'helpdesk.ticket'
_description = 'Customer Support Ticket'
# Here is the magic! We pull in the abstract model's blueprint.
_inherit = ['cancel.reason.template']
name = fields.Char(string="Ticket Subject", required=True)
customer_id = fields.Many2one('res.partner', string="Customer")
# We don't need to write 'cancel_reason' or 'is_cancelled' here.
# Odoo automatically adds them to the 'helpdesk.ticket' database table!
class FleetVehicle(models.Model):
_name = 'fleet.vehicle'
_description = 'Company Vehicle'
# We can reuse the exact same blueprint for a completely different app!
_inherit = ['cancel.reason.template']
license_plate = fields.Char(string="License Plate")
model_id = fields.Many2one('fleet.vehicle.model', string="Model")
In your XML views for both helpdesk.ticket and fleet.vehicle, you can now easily add <field name="cancel_reason"/> and <button name="action_cancel_record" type="object" string="Cancel"/>.
💡 Developer Advice for Odoo 19
Never Try to Search an Abstract Model: Because there is no database table for 'cancel.reason.template', you cannot use ORM methods on it directly. If you try to run self.env['cancel.reason.template'].search([]), Odoo will throw an error. You must search the actual models that inherited it (like helpdesk.ticket).
Keep self Generic: When writing methods inside an Abstract Model, remember that self could represent a Helpdesk Ticket, a Vehicle, or anything else! Never hardcode field names in your Abstract Model methods unless you are 100% sure the inheriting model will have them.
Mixins are Abstract Models: In the Odoo docs, you will frequently see the word "Mixin" (like mail.thread or mail.activity.mixin). A Mixin is just a naming convention for an Abstract Model! They are the exact same thing under the hood.
There are no comments for now.