Odoo 14 同视图内三级层级可视化及一对多关联行内嵌展开功能可行性咨询
Absolutely! You can totally build a three-level hierarchical view right within the same Odoo 14 form—no need to jump to separate form views every time. Let me walk you through two solid approaches, depending on how flexible you need the solution to be:
Approach 1: Quick Native Extension (No Custom JS)
If you need a simple, no-frills solution, you can leverage Odoo's existing view components and a boolean toggle field to show/hide child levels inline.
Step 1: Adjust Your Models
First, add a boolean field to your middle-level model to track expansion state, plus the necessary one2many/many2one relationships:
# Parent Model (Level 1) class ParentModel(models.Model): _name = 'parent.model' name = fields.Char(string="Parent Name") child_level1_ids = fields.One2many('child.level1', 'parent_id', string="Level 1 Children") # Middle Model (Level 2) class ChildLevel1(models.Model): _name = 'child.level1' name = fields.Char(string="Level 1 Child Name") parent_id = fields.Many2one('parent.model', string="Parent") is_expanded = fields.Boolean(string="Expanded", default=False) # Toggle field child_level2_ids = fields.One2many('child.level2', 'level1_id', string="Level 2 Children") # Child Model (Level 3) class ChildLevel2(models.Model): _name = 'child.level2' name = fields.Char(string="Level 2 Child Name") level1_id = fields.Many2one('child.level1', string="Level 1 Parent")
Step 2: Add Toggle Logic
Add a simple method to the middle model to flip the expansion state:
def toggle_expand(self): self.is_expanded = not self.is_expanded
Step 3: Configure the Form View
In your parent model's form view, set up the one2many tree to show/hide the third level based on the toggle:
<field name="child_level1_ids"> <tree editable="bottom"> <field name="name"/> <!-- Toggle button to expand/collapse --> <button name="toggle_expand" type="object" icon="fa-chevron-down" attrs="{'icon': [('is_expanded', '=', True), 'fa-chevron-up'], ('is_expanded', '=', False), 'fa-chevron-down']}" /> <!-- Third-level tree, hidden by default --> <field name="child_level2_ids" attrs="{'invisible': [('is_expanded', '=', False)]}" colspan="4" > <tree> <field name="name"/> </tree> </field> </tree> </field>
Pros: Fast to implement, no custom JS required.
Cons: Limited styling flexibility; third-level data loads with the parent form (might slow down if you have lots of records).
Approach 2: Custom JavaScript Widget (Full Control)
For a smoother, more interactive experience (like dynamic lazy loading of third-level data), build a custom tree widget that handles expansion/collapse natively.
Step 1: Create the Custom JS Widget
Create a file static/src/js/custom_hierarchy_tree.js with this code:
odoo.define('custom_hierarchy_tree.CustomHierarchyTree', function (require) { "use strict"; const TreeView = require('web.TreeView'); const ListRenderer = require('web.ListRenderer'); const core = require('web.core'); const CustomHierarchyListRenderer = ListRenderer.extend({ // Add expand button to rows with child data _renderRow: function (record) { const $row = this._super(record); if (record.data.child_level2_ids?.length > 0) { const $expandBtn = $('<button>', { type: 'button', class: 'btn btn-sm o_expand_btn', html: '<i class="fa fa-chevron-down"></i>', click: () => this._toggleExpand(record, $row) }); $row.find('td:first').prepend($expandBtn); } return $row; }, // Handle expand/collapse and render child tree _toggleExpand: function (record, $row) { const $childContainer = $row.next('.o_child_tree_container'); if ($childContainer.length > 0) { // Toggle existing container visibility $childContainer.toggle(); $row.find('.o_expand_btn i').toggleClass('fa-chevron-down fa-chevron-up'); return; } // Create new container for child tree const $container = $('<tr>', {class: 'o_child_tree_container'}).append( $('<td>', {colspan: this.arch.children.length}).append('<div class="o_child_tree ml-4"/>') ); $row.after($container); // Load and render third-level tree const childDomain = [['level1_id', '=', record.res_id]]; const childView = { type: 'tree', model: 'child.level2', fields: this.fields['child_level2_ids'].relationFields, arch: '<tree><field name="name"/></tree>' }; this.loadFieldView(childView, {domain: childDomain}) .then(view => view.createRenderer($container.find('.o_child_tree')).render()); // Update button icon $row.find('.o_expand_btn i').removeClass('fa-chevron-down').addClass('fa-chevron-up'); } }); const CustomHierarchyTree = TreeView.extend({ config: _.extend({}, TreeView.prototype.config, { Renderer: CustomHierarchyListRenderer }) }); core.view_registry.add('custom_hierarchy_tree', CustomHierarchyTree); return CustomHierarchyTree; });
Step 2: Register the Widget in Your Manifest
Update your module's __manifest__.py to include the JS asset:
{ 'name': 'Custom Hierarchy View', 'version': '1.0', 'depends': ['base', 'web'], 'data': [ 'views/parent_view.xml', ], 'assets': { 'web.assets_backend': [ 'custom_hierarchy_tree/static/src/js/custom_tree_widget.js', ], }, 'installable': True, 'auto_install': False, }
Step 3: Use the Widget in Your View
Modify the parent form view to use your custom widget:
<field name="child_level1_ids" widget="custom_hierarchy_tree"> <tree> <field name="name"/> <!-- Include the third-level field (invisible) to access its data --> <field name="child_level2_ids" invisible="1"/> </tree> </field>
Pros: Smooth dynamic loading, full control over styling/behavior, no extra boolean fields.
Cons: Requires basic JS knowledge; needs testing for edge cases (like large datasets).
Key Notes
- Styling: Add custom CSS to indent the third-level tree or adjust button positioning for better UX.
- Performance: For large datasets, consider adding pagination or lazy loading to the custom widget to avoid slowdowns.
- Permissions: Ensure users have read access to all three models so the data loads correctly.
内容的提问来源于stack exchange,提问作者user1632812

