Django表单提交后表间关联失效、外键约束报错问题排查
问题核心原因
你遇到的所有报错、关联失效问题,核心都是保存各表数据时没有手动绑定外键/多对多关联,Django不会自动把同一次请求里的三个模型数据关联起来,必须手动指定关联关系。
1. 修复models.py错误与不合理设计
原代码存在字段前缀遗漏、命名不匹配问题,且用ManyToManyField不符合你单条关联仅存2个号码的需求,直接用双外键天然限制数量:
from django.db import models class Customer(models.Model): customer_name = models.CharField(max_length=100) customer_email = models.CharField(max_length=100) # 方便后台显示 def __str__(self): return self.customer_name class Phonenumber(models.Model): customer_number = models.CharField(max_length=100) # 补全models前缀,后续保存时会赋值,不需要加null=True belongs_to = models.ForeignKey(Customer, on_delete=models.DO_NOTHING) def __str__(self): return self.customer_number class Connection(models.Model): # 模型名建议单数,Django自动生成复数标识 registered_at = models.DateTimeField(auto_now=True) got_number_at = models.DateTimeField(blank=True, null=True) registered_by = models.ForeignKey(Customer, on_delete=models.DO_NOTHING) # 双外键直接绑定两个号码,天然限制单条关联最多2个号码 number1 = models.ForeignKey(Phonenumber, on_delete=models.DO_NOTHING, related_name="connection_as_number1") number2 = models.ForeignKey(Phonenumber, on_delete=models.DO_NOTHING, related_name="connection_as_number2") def __str__(self): return f"{self.number1} <-> {self.number2}"
2. 修复phonebook_form.py错误
原代码存在字段名不匹配、语法括号错误,修改后支持两个号码同时提交:
from django.forms import ModelForm, TextInput, forms from .models import * class CustomerForm(ModelForm): class Meta: model = Customer # 字段名必须和模型定义一致 fields = ['customer_name', 'customer_email'] widgets = { 'customer_name': TextInput(attrs={'class':'form-control', 'id': 'cus_name'}), 'customer_email': TextInput(attrs={'class':'form-control', 'id': 'cus_email'}) } # 双号码提交表单,符合单会话注册2个号码的需求 class NumberPairForm(forms.Form): number1 = forms.CharField(max_length=100, widget=TextInput(attrs={'class':'form-control', 'id': 'cus_number1'}), label="号码1") number2 = forms.CharField(max_length=100, widget=TextInput(attrs={'class':'form-control', 'id': 'cus_number2'}), label="号码2") class ConnectionForm(ModelForm): class Meta: model = Connection fields = ['got_number_at'] widgets = { 'got_number_at': TextInput(attrs={'class':'form-control', 'id': 'connection_date'}) }
3. 修复views.py逻辑,手动绑定关联
原代码存在语法错误、没有关联绑定、未传上下文给模板的问题:
from django.shortcuts import render from .phonebook_form import * # 所有视图逻辑必须放在函数内 def phonebook_view(request): form1 = CustomerForm() form2 = NumberPairForm() form3 = ConnectionForm() if request.method == "POST": # 双等号才是判断逻辑 form_customer = CustomerForm(request.POST) form_number = NumberPairForm(request.POST) form_connection = ConnectionForm(request.POST) # 三个表单全部校验通过再处理 if form_customer.is_valid() and form_number.is_valid() and form_connection.is_valid(): # 1. 先存客户,拿到客户实例 customer = form_customer.save() # 2. 存两个号码,绑定所属客户 num1 = Phonenumber.objects.create( customer_number = form_number.cleaned_data['number1'], belongs_to = customer ) num2 = Phonenumber.objects.create( customer_number = form_number.cleaned_data['number2'], belongs_to = customer ) # 3. 存关联记录,先不提交到库,绑定所有关联字段后再保存 connection = form_connection.save(commit=False) connection.registered_by = customer connection.number1 = num1 connection.number2 = num2 connection.save() context = {'form1': form1, 'form2': form2, 'form3': form3 } # 必须把context传给模板 return render(request, 'phoneBook/phonebook.html', context)
4. 后台显示配置(解决后台看不到关联的问题)
在app下的admin.py中添加如下配置,即可在后台直接查看所有关联关系:
from django.contrib import admin from .models import * @admin.register(Customer) class CustomerAdmin(admin.ModelAdmin): list_display = ('id', 'customer_name', 'customer_email') @admin.register(Phonenumber) class PhonenumberAdmin(admin.ModelAdmin): list_display = ('id', 'customer_number', 'belongs_to') @admin.register(Connection) class ConnectionAdmin(admin.ModelAdmin): list_display = ('id', 'registered_at', 'got_number_at', 'registered_by', 'number1', 'number2')
最后操作
修改完代码后执行迁移命令更新数据库结构即可:
python manage.py makemigrations python manage.py migrate
如果迁移时提示需要给现有历史数据设置默认值,直接选择1输入0临时处理即可,或者清空之前的测试数据再执行迁移。
内容的提问来源于stack exchange,提问作者Sunny Garden
相关产品推荐
相关产品推荐

