http://www.codeproject.com/Tips/998870/Odoo-custom-view-module
Introduction
The tip covers 2 separate Odoo8 modules
- The view module: creates a simple view that prints sample buttons and a given QWeb template content provided by the other module.
- The demo module: provides an example and a sample QWeb template to use the view module.
The code can be used as a base to create any custom view type for Odoo8.
Background
While implementing Odoo8 for a "training center" we needed a page that an instructor can use to check his schedule, that page would need to appear as a calendar and clearly show start and end time of a given course over an interval, the way we needed to display it is pretty different from the built in calendar so we had to find a way to create a custom calendar view and use it in our module.
The View Module
The module is called web_dummy, the name doesn't matter but we have to note it as it needs to appear in a few places in the code, our module will have the following files:
/static/src/css/dummy.css
For custom view's related css, empty in our case since we won't have a custom look and is just included for completeness.
/static/src//js/dummy.js
For the custom view's logic and actions, in that file we need to create a javascript “class” and initialize it, the example js here was copied from the kanban view, renamed and stripped down and modified as needed for the example.
Our class is instance.web_dummy.DummyView, we need to note that web_dummy has to match the module name web_dummy else this'll result in errors along the lines of “unable to find class”.
<span style="line-height: normal;">instance.web_dummy.DummyView = instance.web.View.extend({….});
</span>
we also need to add it as a web view like that
instance.web.views.add('dummyview', 'instance.web_dummy.DummyView');
init
function is the entry point, typically needs to initialize class variables.
init: function (parent, dataset, view_id, options) {
this._super(parent, dataset, view_id, options);
var self = this;
this.fields_view = {};
this.fields_keys = [];
this.qweb = new QWeb2.Engine();
this.qweb.debug = instance.session.debug;
this.qweb.default_dict = _.clone(QWeb.default_dict);
this.has_been_loaded = $.Deferred();
this.currently_dragging = {};
this.limit = options.limit || 40;
this.add_group_mutex = new $.Mutex();
},
view_loading
function receives a single variable that has the xml view of the calling view (ex: the view from the other module using that view) and should process that variable to load data and bind parameters….etc.
![](https://i-blog.csdnimg.cn/blog_migrate/0985f4045753576ac7d306e0e1ef25d6.png)
view_loading: function(r) {
return this.load_dummyview(r);
},
load_dummyview: function(data) {
this.fields_view = data;
// use default order if defined in xml description
var default_order = this.fields_view.arch.attrs.default_order,
unsorted = !this.dataset._sort.length;
if (unsorted && default_order) {
this.dataset.set_sort(default_order.split(','));
}
this.$el.addClass(this.fields_view.arch.attrs['class']);
this.$buttons = $(QWeb.render("DummyView.buttons", {'widget': this}));
this.$cont = data.arch.children;
if (this.options.$buttons) {
this.$buttons.appendTo(this.options.$buttons);
} else {
this.$el.find('.oe_dummyview_buttons').replaceWith(this.$buttons);
}
this.$el.find('.oe_dummyview_content').append(this.$cont);
this.$groups = this.$el.find('.oe_dummyview_groups tr');
this.fields_keys = _.keys(this.fields_view.fields);
this.add_qweb_template();
this.$cont=this.qweb.render('dummy-content', this.qweb_context);
this.$el.find('.oe_dummyview_content').replaceWith(this.$cont);
this.has_been_loaded.resolve();
this.trigger('dummyview_view_loaded', data);
return $.when();
},
add_qweb_template
and transform_qweb_template
are used to process QWeb templates from the calling module in order to allow loading them inside the custom view's template, this is something specific to the kanban view and this module since both allow loading a qweb template inside the view they provide and may or may not be needed for a custom view...this really depends on the reason to create the view and its specs.
/static/src/xml/web_dummy.xml
Templates the js file can use
In that file we have 2 templates defined, one for the main body (called “DummyView”) and the other for the buttons (called “DummyView.buttons”), names here need to match the names used in the js file, also css classes are important and some of them are used by the js file to place content/buttons...etc
/views/web_dummy.xml
The main layout file, typically used to include the view's js and css files
/__init__.py
Python module entry point, typically used to include other py files for the classes
/__openerp__.py
Odoo module entry point, typically used to define the other modules this module requires to function properly, it also defines the display name, version, category and description of the module alongside with the xml files that need to be included.
/models.py
The actual file that contains our custom python classes.
The tricky part here is that when activating/updating a new module Odoo validates its xml and tries to make sure the view types and fields are valid, that's good but the part with validating the view type requires some work as it compares the name against a static drop down type field called “type” in a class named “ir.ui.view” so we need to append our new type to the list here without modifying the core files, so we'll create a new class called view -or anything that's unique and meaningful to the module- and have it inherit “ir.ui.view” then we need to override its __init__ function to initialize its parent class and add our type as needed like the following
def __init__(self, pool, cr):
super(view, self).__init__(pool, cr)
super(view, self)._columns['type'].selection.append(('dummyview','DummyView'))
The Demo Module
This is a simple Odoo8 module that's used to demonstrate the functionality of the custom view.
That module is called demo_dummy and has the following structure:
/__init__.py
The python module entry point, includes “/models.py”
/__openerp__.py
The main Odoo entry point, defines the module's name, description, category, author and version, also defines the modules this one relies on to function (ex: web_dummy) and the data/template files to include.
/models.py
This file contains the classes(models) that define the data and logic the module relies on to function.
For this example we'll be using a single class called dummy
, with name “demo_dummy.content
” that doesn't have any fields.
class dummy(models.Model): _name = 'demo_dummy.content' _description = 'Dummy content'
/templates.xml
This file defines the menu structures and the view layouts for that module, access needed for each menu item if any (not related to the demonstration)
we'll define a main level menu item called Dummy Example
<menuitem name="Dummy Example" id="demo_dummy.content" />
then we'll define a section menu item called Demo
<menuitem name="Demo" id="demo_dummy.content1" parent="demo_dummy.content"/>
and the most important part here is the following code
![](https://i-blog.csdnimg.cn/blog_migrate/0985f4045753576ac7d306e0e1ef25d6.png)
<record model="ir.ui.view" id="demo_dummy">
<field name="name">demo.dummy</field>
<field name="model">demo_dummy.content</field>
<field name="arch" type="xml">
<dummyview>
<templates>
<t t-name="dummy-content">
<div class="col-md-12">
<h2>Lorem ipsum</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin id lorem fringilla, aliquet orci ac, hendrerit ex. Nullam purus turpis, aliquet nec facilisis at, vehicula non lorem. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Maecenas tincidunt ex metus, ultricies ullamcorper velit molestie eget. In hac habitasse platea dictumst. Aenean rutrum rhoncus turpis sit amet tincidunt. Mauris commodo justo quis quam ultricies, nec blandit nulla interdum. Sed vulputate lacus erat, in auctor ipsum malesuada at. In vehicula velit enim, quis sollicitudin nunc facilisis non. Praesent a metus hendrerit, rutrum turpis sed, commodo justo. Nulla suscipit risus vel felis consectetur consectetur. Morbi mi nunc, tempor sit amet quam id, posuere molestie metus.</p>
<p>In accumsan blandit libero. Aliquam pharetra convallis enim, quis pretium lacus scelerisque ut. Nunc et velit nec ligula pretium molestie. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aliquam in leo eget ante egestas condimentum. Donec lobortis, est non commodo consequat, arcu purus posuere erat, id faucibus sapien arcu quis lectus. Quisque commodo ut arcu ultrices posuere. Phasellus interdum justo non tellus egestas sodales. Mauris bibendum sapien a sem maximus rhoncus. Nulla molestie urna sem, sit amet feugiat est sodales ut. Aliquam eu efficitur sapien. Duis libero libero, elementum et sem lacinia, rhoncus elementum erat. Maecenas euismod tortor et fermentum condimentum. Quisque non efficitur justo. </p>
</div>
</t>
</templates>
</dummyview>
</field>
</record>
here we've defined a view with architecture dummyview (like kanbanview and calendar) with an inner template named “dummy-content” that can contain pretty much any html code, that code will be placed by the logic of the view type as the body of the page
then we define a 3rd level menu item called Content that links to the action
<menuitem name="Content" id="demo_dummy.content2" action="demo_dummy_cont" parent="demo_dummy.content1"/>
Points of Interest
- Odoo8 relies heavily on javascript, Qweb engine and bootstrap 3.
- Typically I'd pick an existing view that's pretty close to what I want to create and start modifying it.
- The "proper" way to modify a model's static dropdown field's options is to inherit then initialize the parent then modify the parent's variable.
- The way to do that is pretty much undocumented, might not be intended or planned to by the developers of the software.
History
The attached code is also publicly avaiable at https://gitlab.com/groups/odoo-dummy-view, can be forked or modified as needed.