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

Django inline_formset:基于另一字段设置字段的QuerySet

嘿,我来帮你搞定Django inline formset里动态设置字段QuerySet的问题!结合你给出的模型结构,我猜你应该是想在InvoiceItem的行内表单里,选了某个Item之后,对应的税组选项只显示这个商品关联的那些——这应该是最常见的场景,如果不是你想要的,随时调整方向哈~

首先先补全你没写完的InvoiceItem模型(毕竟要做关联得先有这个模型):

class InvoiceItem(models.Model):
    invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE)
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    tax_group = models.ForeignKey(TaxGroup, on_delete=models.CASCADE)
    quantity = models.IntegerField(default=1)
    # 可以加单价、小计这类业务字段

接下来分四步实现动态更新QuerySet:


1. 自定义InvoiceItemForm处理初始QuerySet

我们需要在表单初始化时,根据已有实例(编辑场景)或者默认情况,设置tax_group的初始可选范围:

from django import forms
from .models import InvoiceItem, TaxGroup

class InvoiceItemForm(forms.ModelForm):
    class Meta:
        model = InvoiceItem
        fields = ['item', 'tax_group', 'quantity']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 编辑场景:如果已经选了Item,只显示该Item关联的税组
        if self.instance.pk:
            self.fields['tax_group'].queryset = self.instance.item.tax_group.all()
        # 新建场景:默认空选项,等用户选Item后再加载
        else:
            self.fields['tax_group'].queryset = TaxGroup.objects.none()

    # 后端验证!防止前端篡改数据,必须加
    def clean(self):
        cleaned_data = super().clean()
        selected_item = cleaned_data.get('item')
        selected_tax = cleaned_data.get('tax_group')
        
        if selected_item and selected_tax:
            if selected_tax not in selected_item.tax_group.all():
                raise forms.ValidationError("该税组不属于你选中的商品哦")
        return cleaned_data

2. 创建关联的Inline Formset

用Django的inlineformset_factory把自定义表单和Invoice、InvoiceItem关联起来:

from django.forms import inlineformset_factory
from .models import Invoice
from .forms import InvoiceItemForm

InvoiceItemFormSet = inlineformset_factory(
    Invoice,
    InvoiceItem,
    form=InvoiceItemForm,
    extra=1,  # 默认显示1个空的行内表单
    can_delete=True  # 允许删除行内项
)

3. 视图层传递前端需要的映射数据

我们要把所有Item对应的TaxGroup数据传给前端,方便JS动态更新选项:

from django.shortcuts import render
from .models import Invoice, Item

def invoice_edit(request, pk):
    invoice = Invoice.objects.get(pk=pk)
    formset = InvoiceItemFormSet(instance=invoice)
    
    # 构建Item到TaxGroup的映射,转成字典给前端
    item_tax_mapping = {
        item.pk: list(item.tax_group.values('pk', 'name'))
        for item in Item.objects.all()
    }
    
    return render(request, 'invoice_edit.html', {
        'invoice': invoice,
        'formset': formset,
        'item_tax_mapping': item_tax_mapping,
    })

4. 模板层添加JS实现动态更新

在模板里监听Item选择框的变化,动态替换tax_group的选项:

<form method="post">
    {% csrf_token %}
    {{ formset.management_form }}
    {% for form in formset %}
        <div class="invoice-item-row">
            {{ form.as_p }}
            {% if form.instance.pk %}
                {{ form.DELETE }} 删除该项
            {% endif %}
        </div>
    {% endfor %}
    <button type="submit">保存发票</button>
</form>

<script>
// 从视图拿到的Item-TaxGroup映射
const itemTaxMap = {{ item_tax_mapping|safe }};

// 给每个Item选择框绑定事件
document.querySelectorAll('select[name$="-item"]').forEach(itemSelect => {
    // 找到当前行对应的tax_group选择框
    const taxSelect = itemSelect.form.querySelector(`select[name$="-tax_group"]`);
    
    // 页面加载时如果已有选中的Item,初始化TaxGroup选项
    if (itemSelect.value) {
        updateTaxOptions(itemSelect.value, taxSelect);
    }
    
    // 监听Item选择变化
    itemSelect.addEventListener('change', () => {
        updateTaxOptions(itemSelect.value, taxSelect);
    });
});

// 更新TaxGroup选项的工具函数
function updateTaxOptions(itemId, taxSelect) {
    // 清空现有选项
    taxSelect.innerHTML = '';
    
    if (!itemId) return;
    
    // 遍历当前Item对应的TaxGroup,生成新选项
    itemTaxMap[itemId].forEach(tax => {
        const option = document.createElement('option');
        option.value = tax.pk;
        option.textContent = `${tax.name}`;
        taxSelect.appendChild(option);
    });
}
</script>

小提醒:如果是新建发票的视图,逻辑和编辑几乎一样,只要把invoice = Invoice.objects.get(pk=pk)换成invoice = Invoice()即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:47:28