So erstellen Sie benutzerdefinierte Odoo-Module: Ein Entwicklerhandbuch zu OWL, ORM und Vererbung

Entwicklerhandbuch zum Erstellen benutzerdefinierter Odoo-Module. Behandelt Modulstruktur, OWL-Framework, ORM-Vererbung, Ansichten, Sicherheitsregeln, Tests und OCA-Richtlinien.

E

ECOSIRE Research and Development Team

ECOSIRE-Team

19. Februar 20266 Min. Lesezeit1.3k Wörter

So erstellen Sie benutzerdefinierte Odoo-Module: Ein Entwicklerhandbuch zu OWL, ORM und Vererbung

Wenn die 82 offiziellen Module und über 40.000 Community-Module von Odoo Ihre genauen Geschäftsanforderungen nicht abdecken, füllt die Entwicklung benutzerdefinierter Module die Lücke. Dieser Leitfaden behandelt die Grundlagen der Erstellung von Odoo-Modulen im Jahr 2026, einschließlich der Modulstruktur, des OWL-Frontend-Frameworks, ORM-Mustern, Vererbungsmechanismen und Best Practices im Einklang mit den Richtlinien der OCA (Odoo Community Association).

Was ist ein benutzerdefiniertes Odoo-Modul?

Ein benutzerdefiniertes Odoo-Modul ist ein eigenständiges Paket aus Python-Backend-Logik, XML-Ansichtsdefinitionen, JavaScript-Frontend-Komponenten und Sicherheitsregeln, das die Funktionalität von Odoo erweitert. Module können völlig neue Funktionen hinzufügen, bestehendes Verhalten ändern oder Odoo in externe Systeme integrieren. Jedes Teil von Odoo selbst ist ein Modul, wodurch die Architektur von Natur aus erweiterbar ist.

Modulverzeichnisstruktur

Ein gut organisiertes Modul folgt dieser Standardstruktur:

my_custom_module/
├── __init__.py              # Python package init
├── __manifest__.py          # Module metadata and dependencies
├── models/
│   ├── __init__.py
│   └── my_model.py          # Business logic and data models
├── views/
│   ├── my_model_views.xml   # Form, tree, and kanban views
│   └── menu.xml             # Menu items and actions
├── security/
│   ├── ir.model.access.csv  # Access control list
│   └── security.xml         # Record rules and groups
├── data/
│   └── data.xml             # Default data records
├── static/
│   └── src/
│       ├── js/              # OWL components
│       ├── css/             # Stylesheets
│       └── xml/             # QWeb templates
├── wizard/                  # Transient models for wizards
├── reports/                 # QWeb report templates
└── tests/                   # Unit tests

Die Manifestdatei

Die Datei __manifest__.py definiert die Identität Ihres Moduls:

{
    'name': 'My Custom Module',
    'version': '18.0.1.0.0',
    'category': 'Custom',
    'summary': 'Short description of what the module does',
    'description': """
        Long description with details about
        features and configuration.
    """,
    'author': 'ECOSIRE',
    'website': 'https://ecosire.com',
    'license': 'LGPL-3',
    'depends': ['base', 'sale', 'stock'],
    'data': [
        'security/ir.model.access.csv',
        'views/my_model_views.xml',
        'views/menu.xml',
        'data/data.xml',
    ],
    'assets': {
        'web.assets_backend': [
            'my_custom_module/static/src/js/**/*',
            'my_custom_module/static/src/css/**/*',
            'my_custom_module/static/src/xml/**/*',
        ],
    },
    'installable': True,
    'application': False,
    'auto_install': False,
}

Versionskonvention: {odoo_version}.{major}.{minor}.{patch} (z. B. 18.0.1.0.0).

Arbeiten mit dem ORM

Odoos Object-Relational Mapping (ORM) ist die Grundlage aller Backend-Entwicklung. Modelle werden Datenbanktabellen zugeordnet, und das ORM bietet CRUD-Operationen, berechnete Felder, Einschränkungen und Workflow-Management.

Ein Modell definieren

from odoo import models, fields, api

class ProjectTask(models.Model):
    _name = 'my_module.task'
    _description = 'Project Task'
    _order = 'priority desc, create_date desc'

    name = fields.Char(string='Task Name', required=True)
    description = fields.Html(string='Description')
    state = fields.Selection([
        ('draft', 'Draft'),
        ('in_progress', 'In Progress'),
        ('done', 'Done'),
        ('cancelled', 'Cancelled'),
    ], default='draft', tracking=True)
    assigned_to = fields.Many2one('res.users', string='Assigned To')
    deadline = fields.Date(string='Deadline')
    priority = fields.Selection([
        ('0', 'Normal'),
        ('1', 'Important'),
        ('2', 'Urgent'),
    ], default='0')
    tag_ids = fields.Many2many('my_module.tag', string='Tags')
    progress = fields.Float(compute='_compute_progress', store=True)

Referenz zu Feldtypen

| Feldtyp | Python-Typ | Anwendungsfall | |---|---|---| | Char | str | Kurztext (Name, Referenz) | | Text | str | Langer Klartext | | HTML | str | Rich-Text-Inhalt | | Ganzzahl | int | Ganze Zahlen | | Float | schweben | Dezimalzahlen | | Boolescher Wert | bool | Wahr/Falsch-Flags | | Datum | Datum | Datum ohne Uhrzeit | | Datum/Uhrzeit | Datum/Uhrzeit | Datum mit Uhrzeit | | Auswahl | str | Dropdown-Auswahl | | Many2one | int | Link zu einem Datensatz | | One2many | Liste | Umkehrung von Many2one | | Viele2viele | Liste | Link zu mehreren Datensätzen | | Binär | Bytes | Dateianhänge |

Berechnete Felder und Einschränkungen

@api.depends('subtask_ids.state')
def _compute_progress(self):
    for task in self:
        total = len(task.subtask_ids)
        done = len(task.subtask_ids.filtered(
            lambda t: t.state == 'done'
        ))
        task.progress = (done / total * 100) if total else 0

@api.constrains('deadline')
def _check_deadline(self):
    for task in self:
        if task.deadline and task.deadline < fields.Date.today():
            raise ValidationError(
                "Deadline cannot be in the past."
            )

Vererbungsmechanismen

Odoo bietet drei Arten der Vererbung, die jeweils einem anderen Zweck dienen:

1. Klassenvererbung (Erweiterung)

Erweitern Sie ein vorhandenes Modell, indem Sie Felder hinzufügen oder Methoden überschreiben. Dies ist das häufigste Muster.

class SaleOrderExtend(models.Model):
    _inherit = 'sale.order'

    custom_reference = fields.Char(string='Custom Ref')
    approved_by = fields.Many2one('res.users')

    def action_confirm(self):
        # Add custom logic before standard confirmation
        for order in self:
            if order.amount_total > 10000 and not order.approved_by:
                raise UserError("Orders over $10,000 require approval.")
        return super().action_confirm()

2. Prototypenvererbung

Erstellen Sie ein neues Modell, das alle Felder und Methoden aus einem vorhandenen Modell kopiert.

class CustomPartner(models.Model):
    _name = 'my_module.partner'
    _inherit = 'res.partner'   # Copies structure
    _description = 'Custom Partner'

3. Delegationsvererbung

Erstellen Sie ein neues Modell, das über einen Many2one-Link an ein vorhandenes Modell delegiert. Die Felder des übergeordneten Modells werden im untergeordneten Modell transparent angezeigt.

class LibraryMember(models.Model):
    _name = 'library.member'
    _inherits = {'res.partner': 'partner_id'}

    partner_id = fields.Many2one('res.partner', required=True,
                                  ondelete='cascade')
    membership_date = fields.Date()
    member_number = fields.Char()

Das OWL Framework (Frontend)

Odoo 18 verwendet OWL (Odoo Web Library) als Frontend-Framework. OWL ist ein komponentenbasiertes Framework, das React oder Vue ähnelt, aber speziell für die Bedürfnisse von Odoo entwickelt wurde.

Grundlegende OWL-Komponente

/** @odoo-module */

import { Component, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";

class TaskDashboard extends Component {
    static template = "my_module.TaskDashboard";

    setup() {
        this.state = useState({
            tasks: [],
            filter: 'all',
        });
        this.loadTasks();
    }

    async loadTasks() {
        this.state.tasks = await this.env.services.orm.searchRead(
            "my_module.task",
            [["state", "!=", "cancelled"]],
            ["name", "state", "assigned_to", "deadline"]
        );
    }

    get filteredTasks() {
        if (this.state.filter === 'all') return this.state.tasks;
        return this.state.tasks.filter(
            t => t.state === this.state.filter
        );
    }
}

QWeb-Vorlage (XML)

<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
    <t t-name="my_module.TaskDashboard">
        <div class="o_task_dashboard">
            <div class="task-filters">
                <button t-on-click="() => state.filter = 'all'">All</button>
                <button t-on-click="() => state.filter = 'in_progress'">
                    In Progress
                </button>
            </div>
            <div class="task-list">
                <t t-foreach="filteredTasks" t-as="task" t-key="task.id">
                    <div class="task-card">
                        <span t-esc="task.name"/>
                    </div>
                </t>
            </div>
        </div>
    </t>
</templates>

Sicherheitskonfiguration

Jedes Modell benötigt explizite Zugriffsregeln. Ohne sie kann kein Benutzer auf die Daten des Modells zugreifen.

Zugriffskontrollliste (ir.model.access.csv)

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_task_user,task.user,model_my_module_task,base.group_user,1,1,1,0
access_task_manager,task.manager,model_my_module_task,my_module.group_manager,1,1,1,1

Regeln aufzeichnen (security.xml)

<record id="task_own_rule" model="ir.rule">
    <field name="name">Own Tasks Only</field>
    <field name="model_id" ref="model_my_module_task"/>
    <field name="domain_force">
        [('assigned_to', '=', user.id)]
    </field>
    <field name="groups" eval="[(4, ref('base.group_user'))]"/>
</record>

Testen Sie Ihr Modul

Odoo unterstützt Python-Unit-Tests mithilfe des unittest-Frameworks mit Odoo-spezifischen Testklassen:

from odoo.tests.common import TransactionCase

class TestTask(TransactionCase):
    def setUp(self):
        super().setUp()
        self.task = self.env['my_module.task'].create({
            'name': 'Test Task',
            'state': 'draft',
        })

    def test_task_creation(self):
        self.assertEqual(self.task.state, 'draft')
        self.assertEqual(self.task.progress, 0)

    def test_deadline_constraint(self):
        with self.assertRaises(ValidationError):
            self.task.write({
                'deadline': fields.Date.subtract(
                    fields.Date.today(), days=1
                ),
            })

Führen Sie Tests aus mit: odoo-bin -d test_db --test-enable -i my_custom_module --stop-after-init

Häufig gestellte Fragen

F: Wie lange dauert die Erstellung eines benutzerdefinierten Odoo-Moduls? Einfache Module (neue Felder, Basisansichten) dauern 1-3 Tage. Mittlere Module (neue Modelle, Workflows, Berichte) dauern 1-3 Wochen. Komplexe Module (Multimodellsysteme, externe Integrationen, kundenspezifische OWL-Komponenten) dauern 4–12 Wochen. Für Unternehmen, die benutzerdefinierte Module benötigen, aber keine internen Odoo-Entwickler haben, stellen Sie einen erfahrenen Odoo-Entwickler ein aus unserem Team.

F: Soll ich den Odoo-Kerncode ändern oder ein separates Modul erstellen? Erstellen Sie immer ein separates Modul. Das Ändern des Kerncodes beeinträchtigt die Aktualisierbarkeit und führt zu Zusammenführungskonflikten bei Versionsaktualisierungen. Verwenden Sie die Vererbung, um vorhandene Modelle und Ansichten aus Ihrem benutzerdefinierten Modul zu erweitern.

F: Was sind OCA-Richtlinien? Die Odoo Community Association (OCA) veröffentlicht Codierungsstandards für die Modulqualität, einschließlich Namenskonventionen, Dokumentationsanforderungen, Erwartungen an die Testabdeckung und Regeln für den Codestil. Durch die Einhaltung der OCA-Richtlinien wird sichergestellt, dass Ihr Modul wartbar und mit dem breiteren Community-Ökosystem kompatibel ist.

Professionelle Hilfe erhalten

Das Erstellen benutzerdefinierter Odoo-Module erfordert Kenntnisse in Python, JavaScript, PostgreSQL und den Framework-Konventionen von Odoo. Egal, ob Sie eine einfache Felderweiterung oder ein komplexes System mit mehreren Modulen benötigen, die [Odoo-Anpassungsdienste] (/services/odoo/customization) von ECOSIRE liefern produktionsbereite Module, die nach OCA-Standards erstellt wurden.

Kontaktieren Sie uns, um Ihre individuellen Modulanforderungen zu besprechen und einen Kostenvoranschlag für die Entwicklung zu erhalten.

Teilen:
E

Geschrieben von

ECOSIRE Research and Development Team

Entwicklung von Enterprise-Digitalprodukten bei ECOSIRE. Einblicke in Odoo-Integrationen, E-Commerce-Automatisierung und KI-gestützte Geschäftslösungen.

Chatten Sie auf WhatsApp