Building A Internet Site — Odoo Documentation

Creating a simple module¶

In Odoo, obligations are executed by way of creating modules.

Modules customise the behavior of an Odoo installation, either by including new

behaviors or with the aid of altering present ones (including behaviors introduced through other


Odoo’s scaffolding can setup a primary

module. To quickly get started without a doubt invoke:

$ ./odoo-bin scaffold Academy my-modules

This will robotically create a my-modules module directory with an

academy module inner. The listing can be an existing module listing

in case you want, however the module call should be specific within the listing.

A demonstration module¶

We have a “whole” module prepared for set up.

Although it does really not anything we can install it:

  • begin the Odoo server

    $ ./odoo-bin --addons-course addons,my-modules

  • go to http://localhost:8069

  • create a brand new database inclusive of demonstration records

  • to head

  • in the top-right corner get rid of the Installed filter and search for


  • click the Install button for the Academy module

To the browser¶

Controllers interpret browser requests and

ship statistics again.

Add a simple controller and make sure it’s far imported by means of (so

Odoo can locate it):


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

from odoo import httpclass Academy(http.Controller):@http.course('/academy/academy/', auth='public')def index(self, **kw):go back "Hello, world"

Shut down your server (^C) then restart it:

$ ./odoo-bin --addons-route addons,my-modules

and open a page to http://localhost:8069/academy/academy/, you need to see your

“page” appear:


Generating HTML in Python isn’t very best.

The normal solution is templates, pseudo-documents with placeholders and

display good judgment. Odoo permits any Python templating machine, however provides its

very own QWeb templating system which integrates with other


Create a template and make certain the template record is registered inside the happen, and regulate the controller to apply our template:


magnificence Academy(http.Controller):@http.course('/academy/academy/', auth='public')def index(self, **kw):return http.request.render('academy.index', 'teachers': ["Diana Padilla", "Jody Caroll", "Lester Vaughn"],)


<odoo><template identity="index"><name>Academy</title><t t-foreach="instructors" t-as="teacher"><p><t t-esc="instructor"/></p></t></template></odoo>

The templates iterates (t-foreach) on all the lecturers (exceeded thru the

template context), and prints every trainer in its personal paragraph.

Finally restart Odoo and replace the module’s statistics (to install the template)

through going toand clicking Upgrade.

Going to http://localhost:8069/academy/academy/ have to now result in:

Storing data in Odoo¶

Odoo fashions map to database tables.

In the previous phase we just displayed a list of string entered statically

within the Python code. This doesn’t permit modifications or continual storage

so we’ll now move our data to the database.

Defining the statistics version¶

Define a teacher version, and make certain it’s miles imported from so it

is efficaciously loaded:


from odoo import models, fields, apiclass Teachers(fashions.Model):_name = 'academy.instructors'name = fields.Char()

Then setup primary get admission to control for the model

and add them to the show up:


# constantly loaded

'statistics': ['security/ir.model.get entry to.csv','templates.xml',


academy/security/ir.model.get admission to.csv¶



this without a doubt gives examine get right of entry to (perm_read) to all customers (group_id:identification

left empty).


Data documents (XML or CSV) need to be added to the

module occur, Python documents (models or controllers) don’t however have to

be imported from (immediately or indirectly)


the administrator consumer bypasses access control, they have get entry to to all

models although now not given get right of entry to

Demonstration information¶

The 2d step is to add some demonstration statistics to the gadget so it’s

possible to check it effortlessly. This is performed by way of including a demo

facts record, which have to be linked from the show up:


<odoo><record id="padilla" model="academy.teachers"><area name="call">Diana Padilla</subject></record><document identity="carroll" model="academy.teachers"><field name="call">Jody Carroll</field></document><report identification="vaughn" version="academy.instructors"><subject name="name">Lester Vaughn</subject></record></odoo>


Data documents can be used for demo and non-demo records.

Demo data are simplest loaded in “demonstration mode” and may be used for waft

checking out and demonstration, non-demo data are usually loaded and used as

preliminary gadget setup.

In this example we’re the use of demonstration information due to the fact an actual user of the

machine could need to enter or import their very own instructors listing, this list

is best beneficial for checking out.

Accessing the records¶

The closing step is to regulate version and template to use our demonstration statistics:

  • fetch the records from the database rather of getting a static list

  • Because search() returns a set of data

    matching the filter out (“all statistics” here), modify the template to print every

    instructor’s call

  • academy/¶

    magnificence Academy(http.Controller):@http.course('/academy/academy/', auth='public')def index(self, **kw):Teachers = http.request.env['academy.teachers']return http.request.render('academy.index', 'instructors':[]))


    <odoo><template identity="index"><title>Academy</name><t t-foreach="instructors" t-as="instructor"><p><t t-esc="instructor.identity"/> <t t-esc=""/></p></t></template></odoo>

    Restart the server and replace the module (so one can replace the happen

    and templates and cargo the demo document) then navigate to

    http://localhost:8069/academy/academy/. The page ought to look slightly

    extraordinary: names need to truly be prefixed by means of quite a number (the database

    identifier for the trainer).

    Website aid¶

    Odoo bundles a module dedicated to building websites.

    So some distance we’ve used controllers pretty directly, however Odoo eight added deeper

    integration and a few other offerings (e.g. default styling, theming) via the

    website module.

  • first, add internet site as a dependency to academy

  • then upload the website=True flag at the controller, this units up some

    new variables at the request object and

    permits the usage of the internet site layout in our template

  • use the website format within the template

  • academy/¶

    'model': 'zero.1',# any module essential for this one to work successfully

    'depends': ['internet site'],# usually loaded

    'statistics': [


    magnificence Academy(http.Controller):@http.route('/academy/academy/', auth='public', internet site=True)def index(self, **kw):Teachers = http.request.env['academy.teachers']go back http.request.render('academy.index', 'teachers':[]))


    <odoo><template identification="index"><t t-name="website.format"><t t-set="identify">Academy</t><div magnificence="oe_structure"><div elegance="container"><t t-foreach="instructors" t-as="instructor"><p><t t-esc="teacher.identification"/> <t t-esc=""/></p></t></div></div></t></template></odoo>

    After restarting the server while updating the module (that allows you to update the

    happen and template) get admission to http://localhost:8069/academy/academy/ ought to

    yield a nicer looking web page with branding and some of built-in page

    elements (pinnacle-stage menu, footer, …)

    The website layout also affords aid for editing gear: click on

    Sign In (inside the pinnacle-right), fill the credentials in (admin /

    admin through default) then click Log In.

    You’re now in Odoo “proper”: the executive interface. For now click on on

    the Website menu object (pinnacle-left corner.

    We’re again inside the internet site but as an administrator, with get right of entry to to advanced

    enhancing features provided by way of the internet site guide:

    • a template code editor () in which

      you can see and edit all templates used for the current page

    • the Edit button in the pinnacle-left switches to “editing mode” wherein

      blocks (snippets) and wealthy text modifying are to be had

    • a number of different capabilities inclusive of cell preview or SEO

    URLs and routing¶

    Controller methods are associated with routes through the

    course() decorator which takes a routing string and a

    wide variety of attributes to personalize its behavior or security.

    We’ve visible a “literal” routing string, which suits a URL segment precisely,

    but routing strings can also use converter styles which suit bits

    of URLs and make the ones to be had as nearby variables. For instance we are able to

    create a new controller approach which takes a piece of URL and prints it out:


    # New direction

    @http.path('/academy/<name>/', auth='public', internet site=True)

    def teacher(self, name):go back '<h1></h1>'.layout(call)

    restart Odoo, get entry to http://localhost:8069/academy/Alice/ and

    http://localhost:8069/academy/Bob/ and notice the distinction.

    As the name suggests, converter patterns don’t just do extraction, they

    additionally do validation and conversion, so we can change the brand new controller

    to simplest be given integers:


    @http.direction('/academy/<int:identification>/', auth='public', internet site=True)

    def instructor(self, identity):go back '<h1> ()</h1>'.layout(identification, kind(identification).__name__)

    Restart Odoo, access http://localhost:8069/academy/2, word how the vintage value

    was a string, however the new one was transformed to an integers. Try getting access to

    http://localhost:8069/academy/Carol/ and observe that the page was not discovered:

    considering that “Carol” isn’t always an integer, the path become not noted and no route could be


    Odoo presents an extra converter called version which affords information

    at once when given their identity. Let’s use this to create a ordinary web page for

    instructor biographies:


    @http.route('/academy/<version("academy.instructors"):trainer>/', auth='public', website=True)

    def instructor(self, trainer):go back http.request.render('academy.biography', 'individual': instructor)


    <template identification="biography"><t t-call="internet site.layout"><t t-set="title">Academy</t><div magnificence="oe_structure"/><div magnificence="oe_structure"><div elegance="field"><h3><t t-esc=""/></h3></div></div><div class="oe_structure"/></t>


    then exchange the list of version to hyperlink to our new controller:


    <template identification="index"><t t-call="website.format"><t t-set="identify">Academy</t><div class="oe_structure"><div elegance="container"><t t-foreach="teachers" t-as="trainer"><p><a t-attf-href="/academy/ slug(trainer) "><t t-esc=""/></a></p></t></div></div></t>


    Restart Odoo and upgrade the module, then you could go to each trainer’s page.

    As an workout, attempt including blocks to a teacher’s web page to jot down a biography,

    then visit some other trainer’s page and so forth. You will find out, that your

    biography is shared among all teachers, due to the fact blocks are introduced to the

    template, and the biography template is shared among all teachers, when

    one page is edited they’re all edited at the same time.

    Field enhancing¶

    Data that is specific to a file need to be stored on that file, so allow us to

    add a new biography discipline to our instructors:


    class Teachers(fashions.Model):_name = 'academy.teachers'call = fields.Char()biography = fields.Html()


    <template id="biography"><t t-name="website.format"><t t-set="title">Academy</t><div magnificence="oe_structure"/><div class="oe_structure"><div elegance="box"><h3><t t-esc=""/></h3><div><t t-esc="individual.biography"/></div></div></div><div elegance="oe_structure"/></t>


    Restart Odoo and update the views, reload the trainer’s page and… the sphere

    is invisible since it includes not anything.

    For document fields, templates can use a special t-area directive which

    lets in editing the field content material from the website the use of field-unique

    interfaces. Change the man or woman template to use t-field:


    <div class="oe_structure"><div class="box"><h3 t-discipline=""/><div t-area="person.biography"/></div>


    Restart Odoo and upgrade the module, there is now a placeholder underneath the

    trainer’s call and a new area for blocks in Edit mode. Content

    dropped there may be saved inside the corresponding teacher’s biography subject, and

    for this reason particular to that teacher.

    The instructor’s call is also editable, and whilst stored the exchange is seen on

    the index web page.

    t-field can also take formatting alternatives which depend on the precise area.

    For instance if we show the change date for a trainer’s file:


    <div class="oe_structure"><div class="field"><h3 t-discipline=""/><p>Last modified: <i t-discipline="man or woman.write_date"/></p><div t-field="person.biography"/></div>


    it’s far displayed in a completely “computery” way and tough to study, but we may want to

    ask for a human-readable version:


    <div elegance="oe_structure"><div class="box"><h3 t-field=""/><p>Last modified: <i t-subject="individual.write_date" t-alternatives='"format": "long"'/></p><div t-area="man or woman.biography"/></div>


    or a relative display:


    <div elegance="oe_structure"><div magnificence="box"><h3 t-discipline=""/><p>Last modified: <i t-discipline="man or woman.write_date" t-options='"widget": "relative"'/></p><div t-field="character.biography"/></div>


    Administration and ERP integration¶A brief and incomplete advent to the Odoo management¶

    The Odoo management changed into in short visible at some stage in the internet site support phase.

    We can pass lower back to it usingin

    the menu (or Sign In in case you’re signed out).

    The conceptual shape of the Odoo backend is easy:

  • first are menus, a tree (menus can have sub-menus) of facts. Menus

    without youngsters map to…

  • actions. Actions have various kinds: links, reviews, code which Odoo should

    execute or records show. Data display moves are referred to as window actions,

    and inform Odoo to show a given model in keeping with a hard and fast of views…

  • a view has a kind, a vast category to which it corresponds (a list,

    a graph, a calendar) and an architecture which customises the manner the

    model is displayed in the view.

  • Editing within the Odoo administration¶

    By default, an Odoo version is largely invisible to a person. To make it

    seen it should be available via an movement, which itself wishes to be

    reachable, generally thru a menu.

    Let’s create a menu for our version:


    # constantly loaded

    'statistics': ['safety/ir.model.get admission to.csv','templates.xml','views.xml',



    <odoo><report identity="action_academy_teachers" model="ir.moves.act_window"><discipline call="name">Academy teachers</area><subject name="res_model">academy.teachers</field></document><menuitem series="zero" identity="menu_academy" name="Academy"/><menuitem identification="menu_academy_content" parent="menu_academy"name="Academy Content"/><menuitem identity="menu_academy_content_teachers"determine="menu_academy_content"action="action_academy_teachers"/>


    then getting access to http://localhost:8069/internet/ within the top left have to be a menu

    Academy, which is chosen with the aid of default, as it is the first menu,

    and having opened a list of instructors. From the listing it’s miles feasible to

    Create new teacher information, and to interchange to the “shape” through-file


    If there is no definition of a way to present facts (a

    view) Odoo will automatically create a fundamental one

    on-the-fly. In our case it really works for the “list” view for now (best displays

    the trainer’s name) however in the “form” view the HTML biography discipline is

    displayed facet-with the aid of-side with the call area and not given enough space.

    Let’s outline a custom shape view to make viewing and modifying instructor records

    a higher experience:


    <document id="academy_teacher_form" version="ir.ui.view"><discipline call="name">Academy teachers: form</area><subject name="version">academy.instructors</subject><field name="arch" kind="xml"><form><sheet><area call="call"/><subject call="biography"/></sheet></form></subject>


    Relations between models¶

    We have seen a couple of “primary” fields saved immediately within the report. There are

    some of fundamental fields. The 2d

    huge classes of fields are relational and used to hyperlink information to one another

    (inside a version or across fashions).

    For demonstration, allow’s create a courses version. Each direction have to have a

    teacher discipline, linking to a single teacher report, however each trainer can

    teach many guides:


    elegance Courses(models.Model):_name = 'academy.publications'call = fields.Char()teacher_id = fields.Many2one('academy.instructors', string="Teacher")





    allow’s also add perspectives so we can see and edit a path’s trainer:


    <report id="action_academy_courses" version="ir.moves.act_window"><area name="name">Academy courses</discipline><area name="res_model">academy.publications</discipline>


    <document identity="academy_course_search" model="ir.ui.view"><discipline call="call">Academy courses: search</area><area call="model">academy.guides</area><area name="arch" type="xml"><search><discipline name="name"/><area call="teacher_id"/></search></area>


    <report identification="academy_course_list" version="ir.ui.view"><area call="call">Academy publications: list</subject><discipline call="model">academy.guides</area><field call="arch" kind="xml"><tree string="Courses"><discipline call="name"/><field call="teacher_id"/></tree></field>


    <document id="academy_course_form" model="ir.ui.view"><area call="call">Academy courses: shape</field><discipline name="version"></area><area call="arch" kind="xml"><form><sheet><field name="name"/><subject name="teacher_id"/></sheet></shape></subject>

    </report><menuitem sequence="0" id="menu_academy" name="Academy"/>

    <menuitem identification="menu_academy_content" determine="menu_academy"call="Academy Content"/>

    <menuitem id="menu_academy_content_courses"figure="menu_academy_content"action="action_academy_courses"/>

    <menuitem identification="menu_academy_content_teachers"determine="menu_academy_content"motion="action_academy_teachers"/>

    It should also be possible to create new guides at once from a instructor’s

    web page, or to see all the courses they teach, so add

    the inverse courting to the teachers



    class Teachers(models.Model):_name = 'academy.teachers'call = fields.Char()biography = fields.Html()course_ids = fields.One2many('academy.guides', 'teacher_id', string="Courses")class Courses(fashions.Model):_name = 'academy.guides'call = fields.Char()teacher_id = fields.Many2one('academy.teachers', string="Teacher")


    <file id="academy_teacher_form" model="ir.ui.view"><area name="name">Academy teachers: shape</subject><discipline call="version">academy.instructors</subject><subject name="arch" type="xml"><form><sheet><field call="call"/><subject call="biography"/><field name="course_ids"><tree Sstring="Courses" editable="backside"><field name="call"/></tree></area></sheet></shape></field>


    Discussions and notifications¶

    Odoo offers technical models, which don’t immediately satisfy business needs

    however which upload competencies to enterprise objects while not having to construct

    them with the aid of hand.

    One of those is the Chatter system, part of Odoo’s e mail and messaging

    system, that may upload notifications and discussion threads to any version.

    The model definitely has to _inherit

    mail.thread, and add the message_ids discipline to its shape view to show

    the discussion thread. Discussion threads are in keeping with-document.

    For our academy, it makes sense to permit discussing publications to deal with e.g.

    scheduling adjustments or discussions among teachers and assistants:


    'model': 'zero.1',# any module important for this one to work successfully

    'depends': ['website', 'mail'],# usually loaded

    'records': [


    elegance Courses(fashions.Model):_name = 'academy.guides'_inherit = 'mail.thread'call = fields.Char()teacher_id = fields.Many2one('academy.teachers', string="Teacher")


    <report identity="academy_course_form" model="ir.ui.view"><field call="call">Academy publications: form</field><area name="version">academy.publications</subject><subject call="arch" type="xml"><form><sheet><field call="call"/><field name="teacher_id"/></sheet><div elegance="oe_chatter"><field call="message_follower_ids" widget="mail_followers"/><discipline name="message_ids" widget="mail_thread"/></div></shape></subject>


    At the lowest of each course form, there’s now a dialogue thread and the

    opportunity for customers of the machine to leave messages and comply with or unfollow

    discussions connected to precise courses.

    Selling publications¶

    Odoo additionally affords commercial enterprise fashions which permit using or opting in commercial enterprise

    wishes more at once. For instance the website_sale module units up an

    e-trade site based on the products inside the Odoo system. We can without difficulty make

    course subscriptions sellable by means of making our guides specific sorts of


    Rather than the previous classical inheritance, this indicates replacing our

    course version through the product version, and lengthening products in-area (to

    add whatever we need to it).

    First of all we want to feature a dependency on website_sale so we get both

    merchandise (via sale) and the ecommerce interface:


    'version': '0.1',# any module important for this one to paintings efficaciously

    'relies upon': ['mail', 'website_sale'],# continually loaded

    'data': [

    restart Odoo, replace your module, there may be now a Shop section in

    the website, listing a number of pre-filled (thru demonstration statistics) merchandise.

    The 2d step is to update the courses model by using product.template,

    and upload a new class of product for courses:


    'safety/ir.model.get admission to.csv','templates.xml','perspectives.xml','information.xml',


    # handiest loaded in demonstration mode

    'demo': [


    <odoo><record version="product.public.category" identification="category_courses"><field call="name">Courses</discipline><area name="parent_id" ref="website_sale.categ_others"/></file>



    <report identification="course0" model="product.template"><subject name="name">Course 0</area><area call="teacher_id" ref="padilla"/><field call="public_categ_ids" eval="[(4, ref('academy.category_courses'), False)]"/><subject name="website_published">True</field><field name="list_price" kind="float">0</subject><field name="type">carrier</field>


    <file identification="course1" version="product.template"><field name="name">Course 1</area><field call="teacher_id" ref="padilla"/><field call="public_categ_ids" eval="[(4, ref('academy.category_courses'), False)]"/><discipline name="website_published">True</discipline><area name="list_price" kind="float">0</discipline><area name="type">service</discipline>


    <document identity="course2" version="product.template"><discipline call="name">Course 2</area><field call="teacher_id" ref="vaughn"/><discipline name="public_categ_ids" eval="[(four, ref('academy.category_courses'), False)]"/><field name="website_published">True</area><subject name="list_price" kind="go with the flow">0</field><discipline call="type">service</subject>



    class Courses(fashions.Model):_name = 'academy.publications'_inherit = ['mail.thread', 'product.template']name = fields.Char()teacher_id = fields.Many2one('academy.instructors', string="Teacher")

    With this established, a few publications are now available inside the Shop,

    even though they may have to be searched for.


    • to extend a model in-vicinity, it’s inherited without giving it a brand new


    • product.template already makes use of the discussions system, so we can

      take away it from our extension version

    • we’re growing our courses as published by way of default in order that they may be

      seen while not having to log in

    Altering current views¶

    So some distance, we have briefly seen:

    • the creation of latest models

    • the creation of new perspectives

    • the introduction of recent records

    • the alteration of current models

    We’re left with the alteration of present records and the alteration of

    existing views. We’ll do each at the Shop pages.

    View alteration is done with the aid of creating extension perspectives, which are carried out on

    pinnacle of the authentic view and modify it. These alteration views can be added or

    eliminated without editing the authentic, making it less difficult to try things out and

    roll changes back.

    Since our publications are unfastened, there is no cause to display their fee on the

    save page, so we’re going to alter the view and disguise the charge if it’s 0. The

    first challenge is finding out which view shows the charge, this can be performed viawhich we could us read the various

    templates worried in rendering a web page. Going thru a few of them, “Product

    item” seems a likely wrongdoer.

    Altering view architectures is completed in 3 steps:

  • Create a brand new view

  • Extend the view to modify by placing the new view’s inherit_id to the

    modified view’s external identity

  • In the architecture, use the xpath tag to choose and adjust factors

    from the changed view

  • academy/templates.xml¶

     <template id="product_item_hide_no_price" inherit_id="website_sale.products_item"><xpath expr="//div[hasclass('product_price')]/b" position="attributes"><characteristic name="t-if">product.charge &gt; zero</attribute></xpath></template>

    The 2d issue we can alternate is making the product classes sidebar

    visible through default:shall we

    you toggle a tree of product categories (used to filter out the principle show) on

    and off.

    This is finished through the customize_show and lively fields of extension

    templates: an extension template (together with the only we’ve simply created) may be

    customize_show=True. This preference will show the view in the Customize

    menu with a take a look at box, allowing administrators to set off or disable them

    (and without problems customise their internet site pages).

    We in reality want to adjust the Product Categories document and set its default

    to active=”True”:


    <file id="website_sale.products_categories" version="ir.ui.view"><field name="lively" eval="True"/>


    With this, the Product Categories sidebar will robotically be enabled while

    the Academy module is hooked up.

    Related Posts

    Leave a Reply

    Your email address will not be published. Required fields are marked *