Django中如何保存BaseInlineFormSet代码初始化数据并实现权重分配?
Django内联表单集保存全月份权重值解决方案
一、调整表单集基础配置
确保表单集强制生成对应月份数量的表单,避免仅处理用户修改过的实例:
from django.forms import inlineformset_factory # 绑定主模型Variable与子模型MonthAllocat,固定生成对应月份数的表单 VariableMonthAllocationInlineFormSet = inlineformset_factory( Variable, MonthAllocat, form=MonthAllocationForm, formset=FieldTargetSumFormSetMixin, extra=0, # 不生成额外空白表单 min_num=len(MONTH_CHOICES), # 强制生成与月份数匹配的表单数量 validate_min=True, can_delete=False # 禁止删除月份表单 )
二、重写表单集save方法,处理权重分配与全实例保存
在表单集的save方法中,先收集用户修改的权重数据,计算剩余权重并按比例分配给未修改月份,再强制标记所有表单为已修改状态,确保全部实例被保存;同时通过self.instance直接获取关联的Variable实例ID:
class VariableMonthAllocationInlineFormSet(FieldTargetSumFormSetMixin, BaseInlineFormSet): def save(self, commit=True): # 收集用户已修改的月份权重 modified_weights = {} for form in self.forms: if form.cleaned_data: month = form.cleaned_data["month"] modified_weights[month] = form.cleaned_data["weight"] # 计算剩余权重及未修改月份的分配比例 total_modified = sum(modified_weights.values()) remaining_weight = Decimal("100.00") - total_modified unmodified_months = [f.instance.month for f in self.forms if f.instance.month not in modified_weights] per_month_weight = remaining_weight / len(unmodified_months) if unmodified_months else Decimal("0.00") # 给未修改表单填充权重并标记为已修改,确保被保存 for form in self.forms: month = form.instance.month if month not in modified_weights: form.cleaned_data["weight"] = per_month_weight form.changed_data.append("weight") # 调用父类方法保存所有实例 instances = super().save(commit=commit) # 兜底处理新创建Variable时的漏存情况 if commit: variable = self.instance for month_num, _ in MONTH_CHOICES: if not MonthAllocat.objects.filter(variable=variable, month=month_num).exists(): # 无修改时用平均权重,有修改时用分配后的权重 weight = modified_weights.get(month_num, per_month_weight) if modified_weights else Decimal("100.00")/len(MONTH_CHOICES) MonthAllocat.objects.create(variable=variable, month=month_num, weight=weight) return instances
三、优化表单初始化逻辑,绑定默认月份与权重
修改MonthAllocationForm的初始化方法,给新表单绑定默认月份和平均权重,确保表单实例能关联到对应月份:
class MonthAllocationForm(forms.ModelForm): class Meta: model = MonthAllocat fields = ["month", "weight"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 给新创建的表单设置默认月份和平均权重 if not self.instance.pk: initial_month = kwargs.get("initial", {}).get("month") if initial_month: self.instance.month = initial_month self.initial["month"] = initial_month avg_weight = Decimal("100.00") / len(MONTH_CHOICES) self.initial["weight"] = avg_weight self.instance.weight = avg_weight
四、调整Admin内联配置,传递初始月份数据
在Admin内联的get_formset方法中,给新创建的Variable实例预先生成所有月份的表单初始数据:
class MonthAllocationInline(admin.TabularInline): model = MonthAllocat form = MonthAllocationForm formset = VariableMonthAllocationInlineFormSet extra = 0 def get_formset(self, request, obj=None, **kwargs): formset = super().get_formset(request, obj, **kwargs) # 新创建Variable时,生成所有月份的初始表单数据 if obj is None: avg_weight = Decimal("100.00") / len(MONTH_CHOICES) formset.initial = [ {"month": num, "weight": avg_weight} for num, _ in MONTH_CHOICES ] return formset
内容的提问来源于stack exchange,提问作者Brenon 28
相关产品推荐
相关产品推荐

