Bài viết này sẽ giúp bạn xây dựng một module trong odoo với giả định bạn đã có kiến thức nhất định về việc phát triển nền tảng Open Erp hay còn gọi là Odoo.

Để giữ được tính toàn vẹn về ngôn ngữ lập trình, chúng tôi đăng hết bài viết bằng tiếng Anh cũng như cung cấp đường dẫn tới bài viết gốc để phục vụ bạn đọc.

Build an Odoo module – Xây dựng một module Odoo

Both server and client extensions are packaged as modules which are optionally loaded in a database.

Odoo modules can either add brand new business logic to an Odoo system, or alter and extend existing business logic: a module can be created to add your country’s accounting rules to Odoo’s generic accounting support, while the next module adds support for real-time visualisation of a bus fleet.

Everything in Odoo thus starts and ends with modules.

Composition of a module

An Odoo module can contain a number of elements:

Business objects
declared as Python classes, these resources are automatically persisted by Odoo based on their configuration
Data files
XML or CSV files declaring metadata (views or workflows), configuration data (modules parameterization), demonstration data and more
Web controllers
Handle requests from web browsers
Static web data
Images, CSS or javascript files used by the web interface or website

Module structure

Each module is a directory within a module directory. Module directories are specified by using the --addons-path option.


most command-line options can also be set using a configuration file

An Odoo module is declared by its manifest. See the manifest documentation information about it.

A module is also a Python package with a __init__.py file, containing import instructions for various Python files in the module.

For instance, if the module has a single mymodule.py file __init__.py might contain:

from . import mymodule

Odoo provides a mechanism to help set up a new module, odoo.py has a subcommand scaffold to create an empty module:

$ odoo.py scaffold <module name> <where to put it>

The command creates a subdirectory for your module, and automatically creates a bunch of standard files for a module. Most of them simply contain commented code or XML. The usage of most of those files will be explained along this tutorial.


Module creation

Use the command line above to create an empty module Open Academy, and install it in Odoo.

  1. Invoke the command odoo.py scaffold openacademy addons.
  2. Adapt the manifest file to your module.
  3. Don’t bother about the other files.

# -*- coding: utf-8 -*-
    'name': "Open Academy",

    'summary': """Manage trainings""",

    'description': """
        Open Academy module for managing trainings:
            - training courses
            - training sessions
            - attendees registration

    'author': "Your Company",
    'website': "http://www.yourcompany.com",

    # Categories can be used to filter modules in modules listing
    # Check https://github.com/odoo/odoo/blob/master/openerp/addons/base/module/module_data.xml
    # for the full list
    'category': 'Test',
    'version': '0.1',

    # any module necessary for this one to work correctly
    'depends': ['base'],

    # always loaded
    'data': [
        # 'security/ir.model.access.csv',
    # only loaded in demonstration mode
    'demo': [


# -*- coding: utf-8 -*-
from . import controllers
from . import models


# -*- coding: utf-8 -*-
from openerp import http

# class Openacademy(http.Controller):
#     @http.route('/openacademy/openacademy/', auth='public')
#     def index(self, **kw):
#         return "Hello, world"

#     @http.route('/openacademy/openacademy/objects/', auth='public')
#     def list(self, **kw):
#         return http.request.render('openacademy.listing', {
#             'root': '/openacademy/openacademy',
#             'objects': http.request.env['openacademy.openacademy'].search([]),
#         })

#     @http.route('/openacademy/openacademy/objects/<model("openacademy.openacademy"):obj>/', auth='public')
#     def object(self, obj, **kw):
#         return http.request.render('openacademy.object', {
#             'object': obj
#         })


        <!--  -->
        <!--   <record id="object0" model="openacademy.openacademy"> -->
        <!--     <field name="name">Object 0</field> -->
        <!--   </record> -->
        <!--  -->
        <!--   <record id="object1" model="openacademy.openacademy"> -->
        <!--     <field name="name">Object 1</field> -->
        <!--   </record> -->
        <!--  -->
        <!--   <record id="object2" model="openacademy.openacademy"> -->
        <!--     <field name="name">Object 2</field> -->
        <!--   </record> -->
        <!--  -->
        <!--   <record id="object3" model="openacademy.openacademy"> -->
        <!--     <field name="name">Object 3</field> -->
        <!--   </record> -->
        <!--  -->
        <!--   <record id="object4" model="openacademy.openacademy"> -->
        <!--     <field name="name">Object 4</field> -->
        <!--   </record> -->
        <!--  -->


# -*- coding: utf-8 -*-

from openerp import models, fields, api

# class openacademy(models.Model):
#     _name = 'openacademy.openacademy'

#     name = fields.Char()




        <!-- <template id="listing"> -->
        <!--   <ul> -->
        <!--     <li t-foreach="objects" t-as="object"> -->
        <!--       <a t-attf-href="{{ root }}/objects/{{ object.id }}"> -->
        <!--         <t t-esc="object.display_name"/> -->
        <!--       </a> -->
        <!--     </li> -->
        <!--   </ul> -->
        <!-- </template> -->
        <!-- <template id="object"> -->
        <!--   <h1><t t-esc="object.display_name"/></h1> -->
        <!--   <dl> -->
        <!--     <t t-foreach="object._fields" t-as="field"> -->
        <!--       <dt><t t-esc="field"/></dt> -->
        <!--       <dd><t t-esc="object[field]"/></dd> -->
        <!--     </t> -->
        <!--   </dl> -->
        <!-- </template> -->

Object-Relational Mapping

A key component of Odoo is the ORM layer. This layer avoids having to write most SQL by hand and provides extensibility and security services2.

Business objects are declared as Python classes extending Model which integrates them into the automated persistence system.

Models can be configured by setting a number of attributes at their definition. The most important attribute is _name which is required and defines the name for the model in the Odoo system. Here is a minimally complete definition of a model:

from openerp import models
class MinimalModel(models.Model):
    _name = 'test.model'

Model fields

Fields are used to define what the model can store and where. Fields are defined as attributes on the model class:

from openerp import models, fields

class LessMinimalModel(models.Model):
    _name = 'test.model2'

    name = fields.Char()

Common Attributes

Much like the model itself, its fields can be configured, by passing configuration attributes as parameters:

name = field.Char(required=True)

Some attributes are available on all fields, here are the most common ones:

string (unicode, default: field’s name)
The label of the field in UI (visible by users).
required (bool, default: False)
If True, the field can not be empty, it must either have a default value or always be given a value when creating a record.
help (unicode, default: '')
Long-form, provides a help tooltip to users in the UI.
index (bool, default: False)
Requests that Odoo create a database index on the column

Simple fields

There are two broad categories of fields: “simple” fields which are atomic values stored directly in the model’s table and “relational” fields linking records (of the same model or of different models).

Example of simple fields are Boolean, Date, Char.

Reserved fields

Odoo creates a few fields in all models1. These fields are managed by the system and shouldn’t be written to. They can be read if useful or necessary:

id (Id)
the unique identifier for a record in its model
create_date (Datetime)
creation date of the record
create_uid (Many2one)
user who created the record
write_date (Datetime)
last modification date of the record
write_uid (Many2one)
user who last modified the record

Special fields

By default, Odoo also requires a name field on all models for various display and search behaviors. The field used for these purposes can be overridden by setting _rec_name.


Define a model

Define a new data model Course in the openacademy module. A course has a title and a description. Courses must have a title.

Edit the file openacademy/models.py to include a Course class.


from openerp import models, fields, api

class Course(models.Model):
    _name = 'openacademy.course'

    name = fields.Char(string="Title", required=True)
    description = fields.Text()

Data files

Odoo is a highly data driven system. Although behavior is customized using Python code part of a module’s value is in the data it sets up when loaded.


some modules exist solely to add data into Odoo

Module data is declared via data files, XML files with <record> elements. Each <record> element creates or updates a database record.

        <record model="{model name}" id="{record identifier}">
            <field name="{a field name}">{a value}</field>
  • model is the name of the Odoo model for the record
  • id is an external identifier, it allows referring to the record (without having to know its in-database identifier)
  • <field> elements have a name which is the name of the field in the model (e.g. description). Their body is the field’s value.

Data files have to be declared in the manifest file to be loaded, they can be declared in the 'data' list (always loaded) or in the'demo' list (only loaded in demonstration mode).


Define demonstration data

Create demonstration data filling the Courses model with a few demonstration courses.

Edit the file openacademy/demo.xml to include some data.


        <record model="openacademy.course" id="course0">
            <field name="name">Course 0</field>
            <field name="description">Course 0's description

Can have multiple lines
        <record model="openacademy.course" id="course1">
            <field name="name">Course 1</field>
            <!-- no description for this one -->
        <record model="openacademy.course" id="course2">
            <field name="name">Course 2</field>
            <field name="description">Course 2's description</field>

Actions and Menus

Actions and menus are regular records in database, usually declared through data files. Actions can be triggered in three ways:

  1. by clicking on menu items (linked to specific actions)
  2. by clicking on buttons in views (if these are connected to actions)
  3. as contextual actions on object

Because menus are somewhat complex to declare there is a <menuitem> shortcut to declare an ir.ui.menu and connect it to the corresponding action more easily.

<record model="ir.actions.act_window" id="action_list_ideas">
    <field name="name">Ideas</field>
    <field name="res_model">idea.idea</field>
    <field name="view_mode">tree,form</field>
<menuitem id="menu_ideas" parent="menu_root" name="Ideas" sequence="10"


The action must be declared before its corresponding menu in the XML file.

Data files are executed sequentially, the action’s id must be present in the database before the menu can be created.


Define new menu entries

Define new menu entries to access courses and sessions under the OpenAcademy menu entry. A user should be able to

  • display a list of all the courses
  • create/modify courses
  1. Create openacademy/views/openacademy.xml with an action and the menus triggering the action
  2. Add it to the data list of openacademy/__openerp__.py

    'data': [
        # 'security/ir.model.access.csv',
    # only loaded in demonstration mode
    'demo': [


<?xml version="1.0" encoding="UTF-8"?>
        <!-- window action -->
            The following tag is an action definition for a "window action",
            that is an action opening a view or a set of views
        <record model="ir.actions.act_window" id="course_list_action">
            <field name="name">Courses</field>
            <field name="res_model">openacademy.course</field>
            <field name="view_type">form</field>
            <field name="view_mode">tree,form</field>
            <field name="help" type="html">
                <p class="oe_view_nocontent_create">Create the first course

        <!-- top level menu: no parent -->
        <menuitem id="main_openacademy_menu" name="Open Academy"/>
        <!-- A first level in the left side menu is needed
             before using action= attribute -->
        <menuitem id="openacademy_menu" name="Open Academy"
        <!-- the following menuitem should appear *after*
             its parent openacademy_menu and *after* its
             action course_list_action -->
        <menuitem id="courses_menu" name="Courses" parent="openacademy_menu"
        <!-- Full id location:
             It is not required when it is the same module -->


Tham khảo: https://www.odoo.com/documentation/8.0/howtos/backend.html


Please enter your comment!
Please enter your name here