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

Django循环生成表单仅保存最后一条记录问题求助

Django表单批量保存异常排查与解决

问题现象

通过视图生成当月日期列表,在模板中循环渲染对应表单字段,页面展示正常,但提交表单后仅最后一条记录能保存到数据库。

问题原因

  1. 表单字段命名冲突:模板中循环渲染的所有字段(如day_type、scheme)使用相同的name属性,HTTP提交时同名参数只会保留最后一条数据的值。
  2. 单表单无法处理批量数据:视图中仅使用单个ModelForm实例,只能处理一条记录的保存逻辑,无法批量处理多条日期的表单数据。
  3. 字段映射不匹配:模板中隐藏字段名为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 05:46:08