You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Odoo 14 同视图内三级层级可视化及一对多关联行内嵌展开功能可行性咨询

Implementing a Three-Level Hierarchical View in Odoo 14 (Same Form)

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 23:18:13