How to develop new views in Odoo 10

Public Channel / Odoo Experience 2016

By Xavier Morel - Odoo Developer

Share on Social Networks

Share Link

Use permanent link to share in social media

Share with a friend

Please login to send this document by email!

Embed in your website

Select page to start with

27. One More Thing

3. Creating a View Anyway, creating a new view (type)

25. Studio No API for Studio integration yet, no Studio for non-standard view types

22. Caveats Not planned => various holes or annoyances Missing pieces of the puzzle can annoy

2. Warning ● Contains JS/web client stuff later on ● Will not cover web client development in general ● Ask web client team or fp for training, documentation, changelogs

12. Client: initial Our empty view ● Action name ● View switcher if multiple views of the same “category” (multi v mono record) ● Search view

26. API ● mono/multi record switch ● API stability ● do_switch_view takes an explicit type requiring specific pairings ● Web team wants API changes (possibly radical) following Studio

24. Drop-in structures ● Validation mixins ● Client helpers/handlers No drop-in validation or helpers/handlers for common (cross-view) Odoo structures, mainly ● Fields ● Buttons

21. Client: View is a Widget ● About it ● Aside from these, view ~ regular widget ● regular widget’s lifecycle ● Regular widget’s tools (willStart/start/$el) ● Regular web client API (QWeb, RPC) ● Regular JS object (vdom)

1. Developing New Views In odoo 10 Xavier Morel EXPERIENCE 2016 Developing new views Technically always possible... by forking the server. But now: new view types in independent isolated modules A bit odd: never planned, never on any roadmap. Just various changes coming together into ability to create views.

16. Client: Control Panel ● Hooking into the Control panel area ● Provides 3 DOM slots ● Associated with view (swapped in/out as view is switched) ● Empty by default ● Names not requirements but strong suggestions ○ matches standard views ○ may be relevant wrt themes ○ In broad sense: corp redesign (actual sidebar), mobile design (buttons need prominence)

23. Post-processing ● access rights ○ this .is_action_enabled( 'edit' ) Some post-processing code works on a whitelist of view types. ● is_action_enabled ○ Basic access rights information ○ Can current user create/write/delete object of the model ○ does not work as server-side information only provided to whitelist of view types

28. Unrelated bonus format_date(date, 'EEE, MMM d', locale) format_date en_US Sat, Oct 8 ● Not related to rest ● Feature I discovered recently ● I18n -> use babel ○ Format dates -> format_date ○ Short date in year (month abbreviation, weekday abbreviation, day of month) ○ Reference locale -> look good

4. Why? ● generic visualisation ● non-technical customisation(-ish) ● integration Why a view? Why a view instead of a client action? ● Generic visualisation over multiple models & records ● Non-technical customisation via view arch ● Web client integration ○ Web client knows the point and can interact more with view Examples: pivot view, geo view (camptocamp), etc...

7. Server: <field> ● groups ● attributes/attrs/modifiers ● fields_get filtering Server: you probably your fields to be called <field> Fair amount of post-processing on fields ● Groups (remove from view if on model, mark as invisible on view) ● Attributes (readonly, invisible and required) and attrs are transferred to modifiers ● Fields_get is merged into fields_view_get then filtered based on what’s in the view Also onchange, but that’s pretty hard to implement ATM

14. Client: other options ● multi_record: false ● mobile_friendly: true ● require_fields: true Other View widget options ● multi_record: views classified in mono v multi records, view switcher only shows one category at a time ○ warning: no way to categorically switch ○ do_switch_view(view_type) ● mobile_friendly: on mobile devices, the first “mobile-friendly” view is shown instead of the first view ● require_fields ○ Needs full fields_get ○ Either to complement existing filtered fields (example: pivot view) ○ or because it doesn’t use <field>

18. Client: Control Panel “sidebar” var Sidebar = require( 'web.Sidebar' ); render_sidebar: function ($node) { var sidebar = new Sidebar( this , {}); sidebar.appendTo($node); } Sidebar section ● Intended as an actions/meta menu ● Helper object provided ● Optional, can hand-roll sidebar “contents”

29. Unrelated bonus format_date(date, 'EEE, MMM d', locale) format_date en_US Sat, Oct 8 es_ES sáb., oct. 8 fr_FR sam., oct. 8 hi_IN शǓन , अÈतू॰ 8 pt_BR sáb, out 8 zh_CN 周六 , 10 月 8 ● Problem: terms translated but format hardcoded ● Different cultures have different ordering & decorations ● Result looks odd as best, nonsensical at worst

5. Server: view type Required class View(models.Model): _inherit = 'ir.ui.view' type = fields.Selection( selection_add=[( 'myview' , "MyView" )] ) class ActWindowView(models.Model): _inherit = 'ir.actions.act_window.view' view_mode = fields.Selection( selection_add=[( 'myview' , "MyView" )] ) First one is the only one actually required. Second one not strictly necessary, most likely good idea. Used when specifying views very explicitly via view_ids.

19. Client: Control Panel pager var Pager = require( 'web.Pager' ); render_pager: function ($node) { var pager = new Pager( this , size, low, page, options); pager.on( 'pager_changed' , this , function (new_state) { // ... }); pager.appendTo($node); } Pager area ● Configurable helper object ● optional

8. Server: validation Optional (but really useful) from import view_validation @view_validation.validate( 'myview' ) def validate_thing(arch): """ Validates a thing """ if arch.find( ".//field[@name='charlie']" ): return True return False Server validation ● Can define validator functions working on lxml etree ● Validator function can do pretty much anything ● Can use elementpath, xpath of traversal to assert specific items are present in the document ● Warning: definition static, loaded even if module not installed. Should not be an issue except multiple views w/ same type.

13. Client: Search View ● do_search: function (domain, context, groupby) { this .set({ 'domain' : domain, 'context' : context, 'groupby' :groupby }); return this ._fetch(); } ● searchable: false Hook search view, or disable it ● If searchable view, view manager will invoke do_search (can be async) ● Passed domain, context and groupby compiled from action and search view ○ Groupby are extracted from contexts for you ● Default view is searchable with no-op do_search ● Non-searchable views don’t get a search view shown at all (e.g. form view)

6. Server: view arch < record id ="view_myview" model ="ir.ui.view" > < field name ="name" >My View</ field > < field name ="model" >res.users</ field > < field name ="arch" type ="xml" > < myview >???</ myview > </ field > </ record > Congratulation your server-side work is done and you can create your view. Content is whatever you want. Which means it’s not that useful, so you’re not actually done

10. Server: base methods Optional (avoid unless necessary) class Base(models.AbstractModel): _inherit = 'base' myfield = fields.Boolean( "My Field" ) @api.multi def myread(self): """ ... """ ● A “base” model has been introduced, which you can extend ● Every other model inherits from base ● Odoo inheritance now properly dynamic, properties added to an inherited model will be reflected on all inheritors even if created before extension ● Avoid unless really necessary ○ Especially fields as they can have odd side-effects (storage, required fields on db upgrade) ○ Methods pollute ns a bit but less problematic ● Warning: base is an abstract model, bad things happen otherwise

30. Unrelated bonus format_skeleton('MMMEEEdd', date, locale) format_date format_skeleton en_US Sat, Oct 8 Sat, Oct 8 es_ES sáb., oct. 8 sáb., 8 oct. fr_FR sam., oct. 8 sam. 8 oct. hi_IN शǓन , अÈतू॰ 8 शǓन , 8 अÈतू॰ pt_BR sáb, out 8 sáb, 8 de out zh_CN 周六 , 10 月 8 10 月 8 日周六 ● Format_skeleton takes pattern of terms to include ● Locale-dependent mapping to formatted pattern ● Babel 2.3 (April 8th, 2016)

17. Client: Control Panel buttons render_buttons: function ($node) { var $buttons = $( '<div/>' ); $buttons.append( QWeb.render( "MyView.buttons" , { 'widget' : this }) ).on( 'click' , '.my_button_create' , this .on_create) .on( 'click' , '.my_button_import' , this .on_import); $node.append($buttons); } Buttons zone ● Intended for action buttons ● Add content to DOM and hook events on them ● May want to keep references to buttons ○ enable/disable them ○ show/hide them

20. Client: View Widget ● fields_view ● model ● dataset ● do_execute_action ● do_switch_view ● do_show View Widget services ● Fields_view is content of fields_view_get ○ Passed to init method (3rd arg) ○ Available as `this.fields_view` ● Model ○ Setup with the action’s model ○ For RPC ● Cursor-ish, mostly setting up “all records” and “current record” for form ● do_execute_action ○ Asks ancestor action manager to execute an action based on descriptor ○ Mostly useful for buttons ● do_switch_view ○ Takes view type ○ Tries to switch to that view type ● do_show ○ Called when view is shown (~switched to)

9. Server: lxml validators Optional (but really useful) def validate_thing(arch): with misc.file_open(os.path.join( 'my_module' , 'views' , 'myview.rng' )) as f: validator = etree.RelaxNG(etree.parse(f)) if validator.validate(arch): return True for error in validator.error_log: logger.error(error) return False LXML validators Anything includes LXML’s various validators, such as RelaxNG (used by Odoo for XML import validation) ● Parse & load relaxng file ● Validate etree ● Print out validation error ● Could just return `validate` result, but error messages on validation failure can be useful ● Lxml also supports Schematron, DTD and XML Schemas

11. Client: declare widget odoo.define( 'x_test.main' , function (require) { var core = require( 'web.core' ); var View = require( 'web.View' ); var _lt = core._lt; var MyView= View.extend({ icon: 'fa-cogs' , display_name: _lt( "My View" ), }); core.view_registry.add( 'myview' , MyView); }); Client: declare widget ● Subtype of the abstract web.View object ● New type must be added to view registry ● Doesn’t do anything at this point ● Technically display_name and icon not required ○ But default values are empty string and question mark ○ Icon and display_name used for view switcher button (icon and tooltip respectively)

15. Client: URL & history ● this .do_push_state({}); ● do_show: function () { this .do_push_state({}); return this ._super(); } ● reload_content: function () { // ... this .do_push_state({ min: this .current_min, limit: this ._limit }); } URL and history handling ● do_push_state (method of View) will add a new state item to the stack ○ ViewManager automatically adds current view *type* ○ Which is why pushing an empty state is useful (adds new state transitioning to current view type) ● Usually called on do_show to update current view_type ● Can pass map of additional values to store in the URL (and history) e.g. ○ pagination info in list view (when updating list) ○ current record id in form view (when loading record) ● Can call at any moment to further update values


  • 0 Total Views
  • 0 Website Views
  • 0 Embeded Views


  • 0 Social Shares
  • 0 Dislikes

Share count

  • 0 Facebook
  • 0 Twitter
  • 0 LinkedIn
  • 0 Google+