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

django-mptt如何实现admin后台选中父节点时自动勾选对应子节点

解决方案

1 修正表单基础配置错误

你现有表单的Meta类中关联模型配置错误,原本要绑定Deviation模型,错写成了Orgunit,先修正这部分:

from django.forms import ModelForm, widgets
from mptt.forms import TreeNodeMultipleChoiceField

from orgunits.models import Orgunit
from deviations.models import Deviation


class MyForm(ModelForm):
    orgunits = TreeNodeMultipleChoiceField(queryset=Orgunit.objects.all(), widget=widgets.SelectMultiple())

    class Meta:
        model = Deviation # 这里修正为Deviation
        fields = '__all__'

2 后端自动过滤仅保存STORE类型叶子节点

重写表单的clean_orgunits方法,对用户提交的选中节点做处理:遍历所有选中节点,递归获取其所有后代节点,过滤出类型为STORE的叶子节点后返回,保证最终存入多对多关联的只有符合要求的节点:

class MyForm(ModelForm):
    orgunits = TreeNodeMultipleChoiceField(queryset=Orgunit.objects.all(), widget=widgets.SelectMultiple())

    class Meta:
        model = Deviation
        fields = '__all__'
    
    def clean_orgunits(self):
        selected_nodes = self.cleaned_data.get('orgunits', [])
        store_nodes = set()
        for node in selected_nodes:
            # 获取当前节点的所有后代节点(包含自身)
            all_descendants = node.get_descendants(include_self=True)
            # 过滤出STORE类型的叶子节点
            store_nodes.update(all_descendants.filter(type='STORE', rght=models.F('lft')+1))
        return list(store_nodes)

3 前端实现选中父节点自动勾选子节点

在DeviationAdmin中注入自定义JS,实现交互逻辑:选中父节点时自动勾选其所有子节点,取消选中父节点时也同步取消子节点选中状态:

修改Admin代码

from django.contrib import admin

from deviations.forms import MyForm
from deviations.models import Deviation


@admin.register(Deviation)
class DeviationAdmin(admin.ModelAdmin):
    form = MyForm
    
    class Media:
        js = ('js/deviation_admin.js',) # 你需要在静态文件目录下创建这个JS文件

编写deviation_admin.js代码

document.addEventListener('DOMContentLoaded', function() {
    const selectEl = document.querySelector('#id_orgunits');
    const options = Array.from(selectEl.options);
    
    // 构建节点ID到层级、子节点的映射
    const nodeMap = {};
    options.forEach(opt => {
        const nodeId = parseInt(opt.value);
        const level = (opt.textContent.match(/^[\u00A0]+/) || [''])[0].length / 2; // mptt默认用2个空格缩进计算层级
        nodeMap[nodeId] = {
            element: opt,
            level: level,
            children: []
        };
    });
    
    // 构建父子节点关系
    const sortedNodes = Object.values(nodeMap).sort((a,b) => a.level - b.level);
    sortedNodes.forEach(node => {
        if (node.level === 0) return;
        // 匹配最近的层级更小的节点作为父节点
        for (let i = sortedNodes.indexOf(node) -1; i >=0; i--) {
            const prevNode = sortedNodes[i];
            if (prevNode.level < node.level) {
                prevNode.children.push(node);
                break;
            }
        }
    });
    
    // 监听选中变化同步子节点状态
    selectEl.addEventListener('change', function(e) {
        const changedOpts = Array.from(e.target.selectedOptions);
        changedOpts.forEach(changedOpt => {
            const nodeId = parseInt(changedOpt.value);
            const node = nodeMap[nodeId];
            const isChecked = changedOpt.selected;
            // 递归更新所有子节点选中状态
            function setChildrenChecked(parentNode, checked) {
                parentNode.children.forEach(child => {
                    child.element.selected = checked;
                    setChildrenChecked(child, checked);
                });
            }
            setChildrenChecked(node, isChecked);
        })
    });
});

内容的提问来源于stack exchange,提问作者Альберт Александров

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 01:15:04