Django:为InlineFormSet中的字段添加自定义查询集
如何在InvoiceDetail的Inline FormSet中设置自定义Product查询集
没问题,我来帮你搞定这个需求。根据你的模型结构,我分两种最常见的使用场景来给出具体解决方案:Django Admin后台中的Inline配置,以及自定义视图里的InlineFormSet配置。
场景1:在Django Admin中配置
如果你是在Admin后台创建/编辑发票时使用Inline,只需要自定义InvoiceDetailInline类,重写formfield_for_foreignkey方法就能轻松过滤Product的查询集。比如我假设你想只显示当前发票所属公司的产品(如果你的Invoices模型后续会关联Company字段,也可以换成其他过滤逻辑,比如只显示有库存的产品):
from django.contrib import admin from .models import Invoices, InvoiceDetail, Products class InvoiceDetailInline(admin.TabularInline): model = InvoiceDetail extra = 1 # 默认显示的空表单行数 def formfield_for_foreignkey(self, db_field, request, **kwargs): # 只针对product字段修改查询集 if db_field.name == "product": # 获取当前正在编辑的发票ID(新建时为空) invoice_id = request.resolver_match.kwargs.get('object_id') if invoice_id: invoice = Invoices.objects.get(id=invoice_id) # 这里替换成你的实际过滤逻辑,比如筛选该发票所属公司的产品 kwargs["queryset"] = Products.objects.filter(company=invoice.company) else: # 新建发票时,可显示所有产品,或根据当前用户所属公司过滤 kwargs["queryset"] = Products.objects.all() return super().formfield_for_foreignkey(db_field, request, **kwargs) class InvoicesAdmin(admin.ModelAdmin): inlines = [InvoiceDetailInline] admin.site.register(Invoices, InvoicesAdmin) admin.site.register(Products)
场景2:在自定义视图中使用InlineFormSet
如果是在自己写的视图里处理发票创建/编辑,需要先自定义InvoiceDetailForm来修改product字段的查询集,再基于这个表单创建InlineFormSet:
步骤1:自定义关联表单
from django import forms from .models import InvoiceDetail, Products class InvoiceDetailForm(forms.ModelForm): class Meta: model = InvoiceDetail fields = ['product', 'quantity_sold'] def __init__(self, *args, **kwargs): # 取出额外传入的过滤参数(比如公司ID) self.company_id = kwargs.pop('company_id', None) super().__init__(*args, **kwargs) # 根据参数过滤产品查询集 if self.company_id: self.fields['product'].queryset = Products.objects.filter(company_id=self.company_id) else: self.fields['product'].queryset = Products.objects.all()
步骤2:在视图中传递参数并生成FormSet
from django.forms import inlineformset_factory from django.shortcuts import render, redirect from .models import Invoices, InvoiceDetail from .forms import InvoiceDetailForm def create_invoice(request): # 基于自定义表单生成InlineFormSet InvoiceDetailFormSet = inlineformset_factory( Invoices, InvoiceDetail, form=InvoiceDetailForm, extra=1 ) if request.method == 'POST': invoice_form = forms.ModelForm(request.POST, model=Invoices) formset = InvoiceDetailFormSet(request.POST, instance=invoice_form.instance) if invoice_form.is_valid() and formset.is_valid(): invoice = invoice_form.save() formset.save() return redirect('invoice_list') else: # 示例:获取当前用户所属公司ID作为过滤条件 company_id = request.user.company.id invoice_form = forms.ModelForm(model=Invoices) # 给FormSet里的每个表单传入过滤参数 formset = InvoiceDetailFormSet(instance=invoice_form.instance, form_kwargs={'company_id': company_id}) return render(request, 'create_invoice.html', { 'invoice_form': invoice_form, 'formset': formset })
关键注意点
- 过滤逻辑完全可以根据你的实际需求调整,比如换成
Products.objects.filter(in_stock__gt=0)只显示有库存的产品,或者Products.objects.filter(is_active=True)只显示上架产品。 - 如果是新建发票时还没确定关联公司,你可以先让用户选择公司,再通过AJAX动态加载对应产品列表(上面的方案是后端静态过滤的实现,适合不需要动态变化的场景)。
内容的提问来源于stack exchange,提问作者Hossam Salah
相关产品推荐
相关产品推荐

