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

如何在TabularInline的TreeNodeChoiceField中按gruppo_id过滤选项

问题描述

如何根据gruppo_id的值修改conto字段的选项列表?
conto是指向model.ContoCOGE(MPTTModel)所定义的树分支的ForeignKey。每个gruppo对应一棵有特定深度的树,如何在TabularInline的TreeNodeChoiceField中过滤所有树,仅显示对应gruppo_id的树?
调试时,重写的formfield_for_foreignkey(self, db_field, request, **kwargs)方法返回了正确的树,但浏览器渲染页面时,选择字段仍显示所有树。

相关代码

model.py

from mptt.models import MPTTModel, TreeForeignKey
from multigroup.models import Gruppo

class ContoCOGE (MPTTModel):  
    gruppo = models.ForeignKey(Gruppo, on_delete=models.CASCADE,related_name='contoCOCGE_gruppo')
    parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')

class PncGen (models.Model):
    gruppo = models.ForeignKey(Gruppo, on_delete=models.CASCADE)
    #other stuff

class PncRighe (models.Model):
    gruppo = models.ForeignKey(Gruppo, on_delete=models.CASCADE)
    idPnc = models.ForeignKey(PncGen,on_delete=models.CASCADE) #puntatore alla testata
    conto = models.ForeignKey(ContoCOGE,on_delete=models.CASCADE,blank=True, null=True)
    #other stuff

admin.py

from django_mptt_admin.admin import DjangoMpttAdmin

class PncRigheInline(admin.TabularInline):
    model = PncRighe
    autocomplete_fields = ['partitarioRiga','conto']
    extra = 1

    def filter_tree_queryset(self, queryset,request):
        qs = queryset.filter(gruppo_id=int(request.session['gruppo_utente']))
        return qs

    def get_changeform_initial_data(self, request):
        return {'gruppo': int(request.session['gruppo_utente'])}

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == 'gruppo' or db_field.name == 'idRiga' or db_field.name == 'idPnc':
            kwargs['widget'] = forms.HiddenInput()

        if db_field.name == 'conto':
            
            db = kwargs.get("using")
            qs = self.get_field_queryset(db, db_field, request)
            kwargs['queryset']=qs.filter(gruppo_id = request.session['gruppo_utente'] )

@admin.register(PncGen)
class PncGenAdmin(admin.ModelAdmin):
    #...
    inlines = (PncRigheInline,)
解决方案

问题核心在于你给conto字段设置了autocomplete_fields,这会触发Django Admin用AJAX异步加载选项,而非直接使用formfield_for_foreignkey中过滤后的queryset。同时原代码的formfield_for_foreignkey方法缺少返回值,也是导致过滤不生效的直接原因。以下是几种可行解决思路:

方案1:重写ContoCOGE的Admin查询集(保留自动补全)

给ContoCOGE注册Admin,基于session中的gruppo_utente过滤AJAX请求返回的选项:

# admin.py
from django.contrib import admin
from .models import ContoCOGE

@admin.register(ContoCOGE)
class ContoCOGEAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if 'gruppo_utente' in request.session:
            return qs.filter(gruppo_id=request.session['gruppo_utente'])
        return qs

这样autocomplete的AJAX请求会自动返回当前gruppo对应的树节点。

方案2:移除自动补全,修复formfield_for_foreignkey

如果不需要自动补全功能,移除autocomplete_fields中的conto,同时修复formfield_for_foreignkey的返回值问题:

# admin.py
from django_mptt_admin.fields import TreeNodeChoiceField

class PncRigheInline(admin.TabularInline):
    model = PncRighe
    autocomplete_fields = ['partitarioRiga']  # 移除conto
    extra = 1

    # 其他方法保持不变

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == 'gruppo' or db_field.name == 'idRiga' or db_field.name == 'idPnc':
            kwargs['widget'] = forms.HiddenInput()

        if db_field.name == 'conto':
            db = kwargs.get("using")
            qs = self.get_field_queryset(db, db_field, request)
            kwargs['queryset'] = qs.filter(gruppo_id=request.session['gruppo_utente'])
            kwargs['form_class'] = TreeNodeChoiceField  # 指定树选择字段
        
        # 必须返回处理后的字段
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

关键注意点

  • 原代码的formfield_for_foreignkey方法没有返回值,导致Django忽略你的配置,使用默认字段,这是核心bug之一。
  • 使用autocomplete_fields时,选项由独立AJAX请求获取,必须在对应ModelAdmin中过滤查询集,否则会返回全量数据。

内容的提问来源于stack exchange,提问作者bleish

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 23:43:12