求助:Django表单提交的数据无法保存至MySQL数据表
Django表单数据无法保存到MySQL数据库问题解析
问题描述
我正尝试将通过Django表单收集的数据保存到MySQL数据库中。页面逻辑是先让用户输入信息生成员工列表,再保存对应列表项的数据。虽然POST请求里能看到数据,但始终无法存入数据库。推测问题出在保存逻辑,但找不到原因,Django日志和数据库错误日志都没报错。
相关代码
Model代码
class MonthlySheet(models.Model): month = models.IntegerField() year = models.IntegerField() org_name = models.ForeignKey(Establishment, on_delete=models.DO_NOTHING) employee = models.ForeignKey(Employee, on_delete=models.CASCADE, null=True, default=None) wages = models.IntegerField() gross = models.IntegerField() total_work_days = models.IntegerField() present_days = models.IntegerField() calc_twelve = models.DecimalField(max_digits=10, decimal_places=2) calc_eight_three_three = models.DecimalField(max_digits=10, decimal_places=2) calc_diff = models.DecimalField(max_digits=10, decimal_places=2) calc_point_seven_five = models.DecimalField(max_digits=10, decimal_places=2) def get_absolute_url(self): return reverse('monthly_sheet_detail', kwargs={'pk': self.pk}) def save(self, *args, **kwargs): self.pk = f"{self.month}.{self.year}" super().save(*args, **kwargs)
View代码
class AddMonthlySheetView(FormView): template_name = 'add_monthlysheet.html' form_class = CustomMonthlySheetForm model = MonthlySheet def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) last_month, year, total_work_days = set_previous_month() context['last_month'] = last_month context['year'] = year context['total_work_days'] = total_work_days context['establishments'] = Establishment.objects.all() establishment_id = self.request.POST.get('org_name') # Use POST instead of GET if establishment_id: employees = get_employees_from_establishment(establishment_id) context['employees'] = employees return context def form_valid(self, form): month = form.cleaned_data['month'] year = form.cleaned_data['year'] org_name = form.cleaned_data['org_name'] employees = form.cleaned_data['employee'] wages = form.cleaned_data['wages'] gross = form.cleaned_data['gross'] total_work_days = form.cleaned_data['total_work_days'] present_days = form.cleaned_data['present_days'] for employee in employees: MonthlySheet.objects.create( month=month, year=year, org_name=org_name, employee=employee, wages=wages, gross=gross, total_work_days=total_work_days, present_days=present_days ) return redirect('employee_list') def monthly_sheet_detail(request, pk): monthly_sheet = get_object_or_404(MonthlySheet, pk=pk) return render(request, 'monthly_sheet_detail.html', {'monthly_sheet': monthly_sheet})
Form代码
class CustomMonthlySheetForm(forms.ModelForm): month = forms.IntegerField() year = forms.IntegerField() org_name = forms.ModelChoiceField(queryset=Establishment.objects.all(), required=True) employee = forms.ModelMultipleChoiceField(queryset=Employee.objects.filter(isactive=True), required=True, widget=forms.CheckboxSelectMultiple) wages = forms.IntegerField() gross = forms.IntegerField() total_work_days = forms.IntegerField() present_days = forms.IntegerField() class Meta: model = MonthlySheet fields = ('month', 'year', 'org_name', 'employee', 'wages', 'gross', 'total_work_days', 'present_days')
HTML模板代码
{% extends 'base.html' %} {% block title %}Add Monthly Sheet{% endblock %} {% block content %} <div class="content-wrapper"> <div class="container"> <h1>Monthly Sheet</h1> <br> <div class="wide-seperator"></div> <form method="POST" id="select-unit"> {% csrf_token %} <div class="form-field"> <label for="month">For the Month of:</label> <input type="number" id="month" name="month" value="{{ last_month }}" required readonly> <label for="year">in Year:</label> <input type="number" id="year" name="year" value="{{ year }}" required readonly> </div> <br> <div class="form-field"> <label for="org_name">Establishment:</label> <select name="org_name" id="org_name" required> <option value="">---Select Establishment---</option> {% for establishment in establishments %} <option value="{{ establishment.id }}"> {{ establishment.org_name }}</option> {% endfor %} </select> <input type="submit" value="Show"> </div> </form> </div> </div> </div> <div class="monthlysheet-list-container"> <form method="POST"> {% csrf_token %} <input type="hidden" name="month" value="{{ last_month }}"> <input type="hidden" name="year" value="{{ year }}"> {% if employees %} <table> <thead> <tr> <th>Name</th> <th>UAN</th> <th>Wages</th> <th>Gross</th> <th>Total Days</th> <th>Present Days</th> </tr> </thead> <tbody> {% for employee in employees %} <tr> <td>{{ employee.name }}</td> <td>{{ employee.uan }}</td> <input type="hidden" name="employee" value="{{ employee.name }}"> <input type="hidden" name="org_name" value="{{ employee.org_name.id }}"> <td><input type="number" name="wages" value="{{ employee.wages }}" required></td> <td><input type="number" name="gross" value="{{ employee.gross }}" required></td> <td><input type="number" name="total_work_days" value="{{ total_work_days }}" required></td> <td><input type="number" name="present_days" value="{{ present_days }}" required></td> </tr> {% endfor %} </tbody> </table> {% endif %} <input type="submit" value="Save Data"> </form> </div> {% endblock %}
URL配置代码
urlpatterns = [ path('add_monthlysheet/', AddMonthlySheetView.as_view(), name='add_monthlysheet'), path('monthlysheet/<int:pk>/', views.monthly_sheet_detail, name='monthly_sheet_detail'), ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
浏览器控制台截图

问题排查与解决方案
核心问题点
主键类型不匹配
Model中重写save方法,强制将主键设置为"{month}.{year}"格式的字符串,但MySQL默认主键是整数类型,类型冲突导致数据无法写入,且Django内部捕获异常未抛出,所以无日志报错。表单字段值传递错误
HTML中employee隐藏字段传递的是员工姓名,而ModelMultipleChoiceField需要接收员工ID,导致form.cleaned_data['employee']无法解析出有效员工对象。必填计算字段未赋值
Model中calc_twelve等字段无默认值,创建对象时未赋值,触发数据库非空约束,事务回滚后无日志记录。
修复方案
1. 修正主键逻辑
删除自定义save方法中设置主键的代码,使用Django默认自增主键;若需按月份+年份+员工唯一标识,添加联合唯一约束:
class MonthlySheet(models.Model): # ... 原有字段 ... def get_absolute_url(self): return reverse('monthly_sheet_detail', kwargs={'pk': self.pk}) # 移除自定义的save方法中设置pk的代码 class Meta: unique_together = ('month', 'year', 'employee')
2. 修正表单员工字段传递值
将HTML中employee隐藏字段改为传递ID,并添加[]支持多选:
<input type="hidden" name="employee[]" value="{{ employee.id }}">
3. 补充计算字段赋值
在form_valid中根据业务逻辑为计算字段赋值:
def form_valid(self, form): # ... 原有代码 ... for employee in employees: # 示例计算逻辑,根据实际业务调整 calc_twelve = wages * 0.12 calc_eight_three_three = gross * 0.0833 calc_diff = calc_twelve - calc_eight_three_three calc_point_seven_five = wages * 0.75 MonthlySheet.objects.create( # ... 原有字段 ... calc_twelve=calc_twelve, calc_eight_three_three=calc_eight_three_three, calc_diff=calc_diff, calc_point_seven_five=calc_point_seven_five ) # ... 原有代码 ...
4. 调试建议
- 在
form_valid中添加日志打印,确认清洗后的数据是否正确:import logging logger = logging.getLogger(__name__) def form_valid(self, form): logger.debug(f"清洗后的数据: {form.cleaned_data}") # ... 后续代码 ... - 在Django Shell中手动测试创建对象,排查具体异常:
python manage.py shell from yourapp.models import MonthlySheet, Establishment, Employee org = Establishment.objects.first() emp = Employee.objects.first() MonthlySheet.objects.create(month=10, year=2024, org_name=org, employee=emp, wages=5000, gross=6000, total_work_days=22, present_days=20, calc_twelve=600, calc_eight_three_three=499.8, calc_diff=100.2, calc_point_seven_five=3750)
内容的提问来源于stack exchange,提问作者Aquaknight
相关产品推荐
相关产品推荐

