odoo

Authoritative Odoo 19.0 development knowledge. Use when the user mentions Odoo, odoo module, __manifest__, ORM, account.move, res.partner, ir.model, odoo addon, odoo model, odoo field, odoo view, odoo controller, odoo migration, odoo upgrade, or any Odoo development task. Triggers on: "odoo", "odoo module", "odoo model", "odoo ORM", "odoo 19", "odoo addon", "odoo development", "odoo accounting", "odoo customization". Source: odoo/odoo@19.0 on GitHub. Zero hallucination — all patterns verified from source code and official documentation.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "odoo" with this command: npx skills add krishamaze/skills/krishamaze-skills-odoo

Odoo 19.0 Development Skill

Source-verified knowledge of Odoo 19.0 internals — ORM, module structure, core models, field types, decorators, and critical breaking changes from earlier versions. No opinions about business logic. Pure platform facts.


Version

Current stable: 19.0 (default branch on odoo/odoo GitHub as of 2026) Branch naming: 19.0, 18.0, 17.0 — each is a separate long-lived branch. Community edition: odoo/odoo. Enterprise: odoo/enterprise (private).


Module Structure

Official structure from docs.odoo.com/19.0/contributing/development/coding_guidelines:

my_module/
├── __init__.py
├── __manifest__.py
├── models/
│   ├── __init__.py
│   └── my_model.py
├── views/
│   └── my_model_views.xml
├── security/
│   ├── ir.model.access.csv
│   └── my_module_security.xml
├── data/
│   └── my_module_data.xml
├── controllers/
│   ├── __init__.py
│   └── my_controller.py
├── report/
│   └── my_report.xml
└── static/
    └── src/

__manifest__.py

{
    'name': 'My Module',
    'version': '19.0.1.0.0',   # format: <odoo_version>.<major>.<minor>.<patch>.<fix>
    'summary': 'One-line description',
    'description': """Long description""",
    'author': 'Author Name',
    'website': 'https://example.com',
    'category': 'Accounting/Accounting',
    'depends': ['base', 'account'],
    'data': [
        'security/ir.model.access.csv',
        'views/my_views.xml',
    ],
    'demo': ['demo/demo_data.xml'],
    'installable': True,
    'application': False,
    'license': 'LGPL-3',
}

Required: name, depends. Everything else is optional but recommended. version must start with the Odoo major version (19.0.).


ORM — Imports (19.0)

# Standard
from odoo import models, fields, api, Command, _
from odoo.fields import Domain
from odoo.exceptions import UserError, ValidationError, AccessError
from odoo.tools import float_compare, float_is_zero, float_round
from odoo.tools.translate import _

# Registry (CHANGED in 19.0)
from odoo.modules.registry import Registry   # NOT: from odoo import registry

Model Definition

from odoo import models, fields, api

class MyModel(models.Model):
    _name = 'my.model'
    _description = 'My Model'
    _order = 'name asc'
    _rec_name = 'name'

    name = fields.Char(string='Name', required=True)
    active = fields.Boolean(default=True)

Model types:

  • models.Model — persistent, stored in PostgreSQL
  • models.TransientModel — temporary, auto-cleaned (wizards)
  • models.AbstractModel — mixin, no table

To extend an existing model:

class ResPartner(models.Model):
    _inherit = 'res.partner'
    my_field = fields.Char()

Field Types

Read references/fields.md for full parameter reference. Quick types:

FieldPython typeNotes
Charstrmax_length optional
Textstrmulti-line
Htmlstrsanitized HTML
Integerint
Floatfloatdigits=(precision, scale)
Monetaryfloatrequires currency_field
Booleanbool
Datedatestored as DATE in PG
Datetimedatetimestored as TIMESTAMP in PG, always UTC
Selectionstrselection=[('key','Label')]
Many2oneintFK to other model
One2manyrecordsetinverse_name required
Many2manyrecordsetrelation table auto-created
Binarybytesattachment=True for large files

ORM Methods

# Create
record = self.env['my.model'].create({'name': 'Test'})

# Read
record.name
records = self.env['my.model'].browse([1, 2, 3])

# Search
records = self.env['my.model'].search([('name', '=', 'Test')], limit=10, order='name asc')
count = self.env['my.model'].search_count([('active', '=', True)])

# Write
record.write({'name': 'Updated'})

# Unlink
record.unlink()

# sudo
self.env['my.model'].sudo().search([])

# with_company
self.env['my.model'].with_company(company_id).create({})

Decorators

@api.depends('field1', 'field2')          # computed field trigger
def _compute_something(self):
    for record in self:
        record.result = record.field1 + record.field2

@api.onchange('field1')                   # UI-only, not stored
def _onchange_field1(self):
    self.field2 = self.field1 * 2

@api.constrains('field1', 'field2')      # validation, raises ValidationError
def _check_something(self):
    for record in self:
        if record.field1 < 0:
            raise ValidationError("Field1 must be positive")

@api.model                                # class-level method (no self record)
def create(self, vals):
    return super().create(vals)

@api.model_create_multi                   # batch create (preferred over @api.model for create)
def create(self, vals_list):
    return super().create(vals_list)

@api.private                              # NEW in 19.0 — marks method as not RPC-accessible
def _internal_method(self):
    pass

Commands (One2many / Many2many)

from odoo import Command

# Create and link new record
Command.create({'name': 'New'})

# Link existing record (Many2many)
Command.link(record.id)

# Unlink (Many2many — remove from relation only)
Command.unlink(record.id)

# Delete record
Command.delete(record.id)

# Replace all records
Command.set([id1, id2, id3])

# Clear all
Command.clear()

# Update existing
Command.update(record.id, {'name': 'Updated'})

Domain Syntax

# Standard domain
[('field', 'operator', value)]

# Operators: =, !=, <, >, <=, >=, in, not in, like, ilike, not like, not ilike, =like, =ilike, any, not any

# Logical operators
['&', ('a', '=', 1), ('b', '=', 2)]   # AND (default)
['|', ('a', '=', 1), ('b', '=', 2)]   # OR
['!', ('a', '=', 1)]                   # NOT

# New in 17+: Domain class
from odoo.fields import Domain
d = Domain('field', '=', value)
combined = d & Domain('other', '!=', False)

Critical Breaking Changes by Version

Read references/breaking-changes.md for full list. Critical ones:

Broken in 17.0

  • name_get() deprecated → override _compute_display_name instead
  • read_group() deprecated → use _read_group() (internal) or formatted_read_group() (public)
  • group_operator field attr deprecated → use aggregator
  • Translations now stored as JSONB, not in database table

Broken in 18.0

  • group_operator produces deprecation warning — must use aggregator

Broken in 19.0

  • read_group removed from public API
  • name_get() removeddisplay_name is the only way
  • odoo.osv deprecated
  • record._cr, record._context, record._uid deprecated (use self.env.cr, self.env.context, self.env.uid)
  • HTTP routes: type='json' → must be type='jsonrpc'
  • res.partner.title model removed
  • from odoo import registryfrom odoo.modules.registry import Registry
  • UoM: use relative_uom_id for direct unit relationships
  • res.groups.privilege replaces ir.module.category for group categories
  • Demo data not loaded by default — must be explicitly requested
  • ORM code moved to odoo/orm/ subpackage (internal restructure)

Reference Files

Load on demand:

FileLoad when
references/fields.mdNeed full field parameter reference
references/accounting.mdWorking with account.move, account.payment, account.journal
references/breaking-changes.mdUpgrading or porting modules between versions
references/security.mdAccess rights, record rules, ir.model.access.csv

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

uv-python-2026

No summary provided by upstream source.

Repository SourceNeeds Review
General

camoufox-2026

No summary provided by upstream source.

Repository SourceNeeds Review
General

docker-vps-2026

No summary provided by upstream source.

Repository SourceNeeds Review
General

project-memory

No summary provided by upstream source.

Repository SourceNeeds Review