Django Formset实现模型选择字段预填充、不可编辑及已有数据回填
解决方案
原代码失效原因
你之前的预填充逻辑存在两个核心问题:
- 存在已有数据时,modelformset会优先加载查询到的记录作为前N个表单,后续才是额外新增的表单,表单顺序和
AGE_GROUPS的顺序没有对应关系,按索引赋值会出现错位 - 判断条件
form.fields['age_group'] == None逻辑错误,fields是表单的字段定义对象,永远不会为None,所以赋值逻辑从未触发
1. 修改表单类,设置年龄组为不可编辑
我们可以将年龄组设为隐藏字段,页面直接显示文本值,既保证不可编辑,也更美观:
class DataForm(forms.ModelForm): class Meta: model = Data fields = ['age_group', 'male', 'female'] widgets = { # 年龄组设为隐藏字段,页面单独显示值即可 'age_group': forms.HiddenInput() }
如果你希望保留下拉框样式,只禁用编辑,将widget改为forms.Select(attrs={'disabled': 'disabled'})即可,Django会自动忽略用户对禁用字段的修改,使用初始值入库。
2. 调整视图逻辑,实现预填充和数据回填
以下代码默认你是针对指定dataset、indicator、facility、month的场景录入数据,几个固定字段的取值请根据你的实际业务逻辑替换:
def home(request): # 以下为当前业务场景的固定值,请替换为你实际的获取逻辑 current_dataset = Dataset.objects.get(id=xxx) current_indicator = Indicator.objects.get(id=xxx) current_facility = "你的机构值" current_month = "2024-05" # 获取当前场景下已有的所有年龄组数据,转为字典方便快速查询 existing_data = Data.objects.filter( dataset=current_dataset, indicator=current_indicator, facility=current_facility, month=current_month ).in_bulk(field_name='age_group') # 构造覆盖所有年龄组的初始数据,自动回填已有记录的男女数值 initial = [] for age_val, age_label in AGE_GROUPS: row = {'age_group': age_val} if age_val in existing_data: row['male'] = existing_data[age_val].male row['female'] = existing_data[age_val].female initial.append(row) # 初始化表单集,用自定义的初始数据,禁用默认的查询集加载 DataFormset = forms.modelformset_factory(Data, form=DataForm, extra=len(AGE_GROUPS)) data_formset = DataFormset( request.POST or None, queryset=Data.objects.none(), initial=initial ) # 处理提交逻辑,避免重复创建数据 if request.method == 'POST': if data_formset.is_valid(): for record in data_formset.cleaned_data: # 存在对应年龄组记录则更新,不存在则新建 Data.objects.update_or_create( dataset=current_dataset, indicator=current_indicator, facility=current_facility, month=current_month, age_group=record['age_group'], defaults={ 'male': record['male'], 'female': record['female'] } ) # 保存成功后的跳转逻辑自行补充 return redirect('home') return render(request, 'home.html', {'formset': data_formset})
3. 模板表格渲染示例
<form method="post"> {% csrf_token %} {{ formset.management_form }} <table> <thead> <tr> <th>年龄组</th> <th>男性人数</th> <th>女性人数</th> </tr> </thead> <tbody> {% for form in formset %} <tr> <td> {# 直接显示年龄组文本 #} {{ form.initial.age_group }} {# 渲染年龄组隐藏字段 #} {{ form.age_group }} </td> <td>{{ form.male }}</td> <td>{{ form.female }}</td> {% for error in form.errors.values %} <td style="color:red">{{ error.0 }}</td> {% endfor %} </tr> {% endfor %} </tbody> </table> <button type="submit">保存数据</button> </form>
内容的提问来源于stack exchange,提问作者Abedy
相关产品推荐
相关产品推荐

