Django循环生成表单仅保存最后一条记录问题求助
Django表单批量保存异常排查与解决
问题现象
通过视图生成当月日期列表,在模板中循环渲染对应表单字段,页面展示正常,但提交表单后仅最后一条记录能保存到数据库。
问题原因
- 表单字段命名冲突:模板中循环渲染的所有字段(如
day_type、scheme)使用相同的name属性,HTTP提交时同名参数只会保留最后一条数据的值。 - 单表单无法处理批量数据:视图中仅使用单个
ModelForm实例,只能处理一条记录的保存逻辑,无法批量处理多条日期的表单数据。 - 字段映射不匹配:模板中隐藏字段名为
data(模型对应字段是date),且official_hours、work_hours需要拼接的时间段字段没有正确处理。
修复步骤
1. 修改模板:为字段添加索引标识
将循环中的字段名改为带索引的格式,确保每条记录的字段唯一,同时修正字段名与模型的匹配:
<div class="card-body"> <form method="POST" enctype="multipart/form-data"> {% csrf_token %} <div class="row p-3 text-center"> {% include 'vita/messages.html' %} <div class="text-center p-2"> <a role="button" class="btn btn-info" href='terminarz?month={{today.month}}&year={{btn_today}}'>{{ btn_today }}</a> <a role="button" class="btn btn-info" href='terminarz?month={{today.month}}&year={{ btn_today_1 }}'>{{ btn_today_1 }}</a> <a role="button" class="btn btn-info" href='terminarz?month={{today.month}}&year={{ btn_today_2 }}'>{{ btn_today_2 }}</a> </div> {% for nr, month in months.items %} <div class="col text-center"> {% if btn_y == btn_today_1 %} <a role="button" class="btn btn-primary p-2" href="terminarz?month={{nr}}&year={{btn_today_1}}">{{month|upper}}</a> {% elif btn_y == btn_today_2 %} <a role="button" class="btn btn-primary p-2" href="terminarz?month={{nr}}&year={{btn_today_2}}">{{month|upper}}</a> {% else %} <a role="button" class="btn btn-primary p-2" href="terminarz?month={{nr}}&year={{btn_today}}">{{month|upper}}</a> {% endif %} </div> {% endfor %} </div> <table class="table table-striped table-sm table-responsive"> <thead class="text-light" style="background: #26396F;"> <tr> <th>Data</th> <th class="text-center">Dzień pracy</th> <th class="text-center">Godziny oficjalne</th> <th class="text-center">Godziny pracy</th> <th class="text-center">Przedział</th> <th class="text-center">Ilość wizyt</th> </tr> </thead> <tbody> {% for index, (date, day) in date_list %} <tr> <td class="p-1"> <a href="/panel/{{ date }}"> <b>{{ date }}</b> - {% if day == 'Sobota' or day == 'Niedziela' %} <span class="text-danger">{{ day }}</span> {% else %} <span class="text-success">{{ day }}</span> {% endif %} </a> <input type="hidden" name="schedule-{{index}}-date" value="{{date}}" /> </td> <td class="p-1"> <select name="schedule-{{index}}-day_type"> {% if day == 'Sobota' or day == 'Niedziela' %} <option value="Wolny" selected>Wolny</option> <option value="Pracujący">Pracujący</option> {% else %} <option value="Pracujący" selected>Pracujący</option> <option value="Wolny">Wolny</option> {% endif %} </select> </td> {% if day == 'Sobota' or day == 'Niedziela' %} <td></td> <td></td> <td></td> <td></td> <input type="hidden" name="schedule-{{index}}-official_hours" value="" /> <input type="hidden" name="schedule-{{index}}-work_hours" value="" /> <input type="hidden" name="schedule-{{index}}-scheme" value="" /> {% else %} <td class="p-1 text-center"> <input name="schedule-{{index}}-official_hours_start" type="time" value="08:00" />- <input name="schedule-{{index}}-official_hours_end" type="time" value="19:00" /> </td> <td class="p-1 text-center"> <input name="schedule-{{index}}-work_hours_start" type="time" value="08:00" />- <input name="schedule-{{index}}-work_hours_end" type="time" value="21:00" /> </td> <td class="p-1 text-center"> <select name="schedule-{{index}}-scheme"> <option value="10">10 min</option> <option value="15">15 min</option> <option value="20">20 min</option> <option value="25">25 min</option> <option value="30" selected>30 min</option> </select> </td> <td class="p-1 text-center">0</td> {% endif %} </tr> {% endfor %} </tbody> </table> <div class="text-center"> <input class="btn btn-success" type="submit" name="update_schedule" value="Uaktualnij terminarz" /> </div> </form> </div>
2. 视图修改:批量处理表单数据
在POST请求中,遍历所有提交的记录,拼接字段值并批量保存:
def terminarz(request): today = datetime.now() now = date.today() locale.setlocale(locale.LC_TIME, 'pl_PL') def months(): months = {'1': 'Styczeń', '2': 'Luty', '3': 'Marzec', '4': 'Kwiecień', '5': 'Maj', '6': 'Czerwiec', '7': 'Lipiec', '8': 'Sierpień', '9': 'Wrzesień', '10': 'Październik', '11': 'Listopad', '12': 'Grudzień'} return months def days_of_month_list(): if request.GET.get('year') and request.GET.get('month'): y = int(request.GET.get('year')) m = int(request.GET.get('month')) else: y = today.year m = today.month date_list = [] for d in range(1, monthrange(y, m)[1] + 1): x = '{:04d}-{:02d}-{:02d}'.format(y, m, d) dayName = datetime.strptime(x, '%Y-%m-%d').weekday() date_list.append((x, calendar.day_name[dayName].capitalize())) return date_list months = months() date_list = days_of_month_list() btn_today = today.year btn_today_1 = today.year + 1 btn_today_2 = today.year + 2 btn_y = int(request.GET.get('year', today.year)) if request.method == 'POST': schedules = [] for i in range(len(date_list)): date_str = request.POST.get(f'schedule-{i}-date') if not date_str: continue day_type = request.POST.get(f'schedule-{i}-day_type') official_start = request.POST.get(f'schedule-{i}-official_hours_start') official_end = request.POST.get(f'schedule-{i}-official_hours_end') work_start = request.POST.get(f'schedule-{i}-work_hours_start') work_end = request.POST.get(f'schedule-{i}-work_hours_end') scheme = request.POST.get(f'schedule-{i}-scheme', "") # 拼接时间段字段 official_hours = f"{official_start}-{official_end}" if official_start and official_end else "" work_hours = f"{work_start}-{work_end}" if work_start and work_end else "" # 创建或更新记录,避免重复 schedule, created = DoctorSchedule.objects.get_or_create( date=datetime.strptime(date_str, '%Y-%m-%d').date(), defaults={ 'day_type': day_type, 'official_hours': official_hours, 'work_hours': work_hours, 'scheme': scheme } ) if not created: schedule.day_type = day_type schedule.official_hours = official_hours schedule.work_hours = work_hours schedule.scheme = scheme schedule.save() schedules.append(schedule) messages.success(request, "Terminarz został zaktualizowany") context = { 'today': today, 'now': now, 'months': months, 'date_list': date_list, 'btn_today': btn_today, 'btn_today_1': btn_today_1, 'btn_today_2': btn_today_2, 'btn_y': btn_y } return render(request, "vita/panel/terminarz.html", context)
3. 优化说明
- 使用
get_or_create避免重复创建相同日期的记录,同时支持更新已有数据。 - 模板中字段名添加索引后缀,确保每条记录的参数唯一。
- 视图中循环处理所有提交的记录,批量完成创建或更新操作。
内容的提问来源于stack exchange,提问作者Erni
相关产品推荐
相关产品推荐

