在Django Admin后台无法访问含Foreign Key的表
Django外键关联引发Operational Error问题排查
我是Django新手,跟着在线课程自学开发了一个项目,最近碰到了外键相关的问题:在Django Admin后台访问带外键属性的模型时,触发了Operational Error错误(见附图)。想问问是不是漏了什么步骤或者代码?
以下是我的models.py代码:
import sys from django.utils.timezone import now try: from django.db import models except Exception: print("There was an error loading django modules. Do you have django installed?") sys.exit() from django.conf import settings import uuid from django.core.validators import RegexValidator import datetime from django.utils.text import gettext_lazy as _ #Specifications of Donor class Donor(models.Model): donor_id=models.IntegerField(blank=False, unique=True, primary_key= True) donor_firstname=models.CharField(blank=False,max_length=60) donor_surname=models.CharField(blank=False,max_length=60) donor_dob=models.DateField(blank=False,default=datetime.date.today) DONOR_GENDERS=[ ('Male','Male'), ('Female','Female') ] donor_gender=models.CharField(blank=False,max_length=60,choices=DONOR_GENDERS) donor_contact=models.IntegerField(blank=False,validators=[RegexValidator(regex='^[0-9]{10}$',message=_('Invalid Phone Number'))]) donor_email=models.EmailField(blank=False) donor_medical_history=models.CharField(blank=True,max_length=300) def __str__(self): return "Donor ID: " + self.donor_id + "Donor First Name: " + self.donor_firstname + \ "Donor Surame: " + self.donor_surname + "Donor Date of Birth: " + self.donor_dob + \ "Donor Gender: " + self.donor_gender + "Donor Contact Phone Number: " + self.donor_contact+\ "Donor Email: " + self.donor_email + "Donor medical History: " + self.donor_medical_history class Hospitals(models.Model): hospital_id=models.IntegerField(blank=False, unique=True, primary_key= True) hospital_name=models.CharField(blank=False,max_length=60) hospital_contact_person=models.CharField(blank=False,max_length=60) hospital_address=models.CharField(blank=False,max_length=250) hospital_city=models.CharField(blank=False,max_length=60) hospital_postal_code=models.CharField(max_length=6,validators=[RegexValidator(regex='^[0-9]{6}$',message=_('Invalid Postal Code'))]) def __str__(self): return "Hospital ID: " + self.hospital_id + "Hospital Name: " + self.hospital_name + \ "Hospital Address: " + self.hospital_address + "Hospital City: "+ self.hospital_city+ \ "Hospital Postal Code: "+ self.hospital_postal_code + "Hospital Contact Person: "+ self.hospital_contact_person class BloodBanks(models.Model): blood_bank_id=models.IntegerField(blank=False, unique=True, primary_key= True) blood_bank_name=models.CharField(blank=False,max_length=60) blood_bank_contact_person=models.CharField(blank=False,max_length=60) blood_bank_address=models.CharField(blank=False,max_length=250) blood_bank_city=models.CharField(blank=False,max_length=60) blood_bank_postal_code=models.CharField(max_length=6,validators=[RegexValidator(regex='^[0-9]{6}$',message=_('Invalid Postal Code'))]) def __str__(self): return "Blood Bank ID: "+ self.blood_bank_id+ "Hospital Name: "+ self.blood_bank_name + \ "Blood Bank Address: "+ self.blood_bank_address+ "Blood Bank City: "+ self.blood_bank_city + \ "Blood Bank Postal Code: "+ self.blood_bank_postal_code +"Blood Bank Contact Person: "+ self.blood_bank_contact_person class Storage(models.Model): storage_id=models.IntegerField(blank=False, unique=True, primary_key= True) capacity=models.PositiveBigIntegerField() current_stock_levels = models.CharField(max_length=200) test_date=models.DateField(blank=False, auto_now_add=True) container_number=models.IntegerField(blank=False) cell_number=models.IntegerField(blank=False) hospital=models.ForeignKey(Hospitals,on_delete=models.CASCADE) bloodbank=models.ForeignKey(BloodBanks,on_delete=models.CASCADE) def __str__(self): return "Capacity:" + self.capacity + "Current Stock Level: "+ self.current_stock_levels+\ "Test Date: " + self.test_date + "Container Number: "+ self.container_number + \ "Cell Number: "+ self.cell_number #Specifications of Blood class BloodBag(models.Model): bag_id=models.IntegerField(blank=False, unique=True, primary_key=True) BLOOD_GROUPS=[ ('A+','A Positive'), ('A-','A Negative'), ('B+','B Positive'), ('B-','B Negative'), ('AB+','AB Positive'), ('AB-','AB Negative'), ('O+','O Positive'), ('O-','O Negative') ] BLOOD_STATUS=[ ('Available','Available'), ('Reserved','Reserved'), ('Unavailable','Unavailable') ] blood_group=models.CharField(blank=False,max_length=3,choices=BLOOD_GROUPS) blood_pressure=models.FloatField(blank=False) blood_pulse=models.IntegerField(blank=False) blood_date_of_donation=models.DateField(blank=False,default=datetime.date.today) blood_haemoglobin=models.FloatField(blank=False) blood_expiry_date=models.DateField(blank=False,default=datetime.date.today) blood_collection_date=models.DateField(blank=False,default=datetime.date.today) blood_storage_location=models.CharField(blank=False,max_length=250) blood_current_status=models.CharField(blank=False,max_length=11,choices=BLOOD_STATUS) # digital blood cross-matching attributes abo_group = models.CharField(max_length=10, null=True, blank=True) rh_factor = models.CharField(max_length=10, null=True, blank=True) cross_match_results = models.CharField(max_length=20, null=True, blank=True) storage=models.ForeignKey(Storage,on_delete=models.CASCADE) def __str__(self): return f"Bag ID: {self.bag_id}, Blood Group: {self.blood_group}, \ Blood pressure: {self.blood_pressure}, Blood Pulse: {self.blood_pulse}, \ Blood Donation: {self.blood_date_of_donation}, Blood Haemoglobin: {self.blood_haemoglobin}, \ Blood Expire Date: {self.blood_expiry_date}, Blood Collection Date: {self.blood_collection_date},\ Blood Storage Location: {self.blood_storage_location}, Blood Current Status: {self.blood_current_status},\ Blood Bag ABO: {self.abo_group}, Blood rh_factor: {self.rh_factor},\ Blood Cross Match Results: {self.cross_match_results}" class BloodRequest(models.Model): REQUEST_BLOOD_GROUPS=[ ('A+','A Positive'), ('A-','A Negative'), ('B+','B Positive'), ('B-','B Negative'), ('AB+','AB Positive'), ('AB-','AB Negative'), ('O+','O Positive'), ('O-','O Negative') ] request_id=models.IntegerField(blank=False,unique=True,primary_key=True) request_name=models.CharField(blank=False,max_length=60) request_contact=models.IntegerField(blank=False,validators=[RegexValidator(regex='^[0-9]{10}$',message=_('Invalid Phone Number'))]) request_blood_type= models.CharField(blank=False,max_length=3,choices=REQUEST_BLOOD_GROUPS) request_quantity=models.PositiveBigIntegerField() request_status = models.CharField(max_length=20, default="Pending") date_of_request=models.DateField(blank=False,default=datetime.date.today) date_of_requirement=models.DateField(blank=False,default=datetime.date.today) bloodbag=models.ForeignKey(BloodBag,on_delete=models.CASCADE) def __str__(self): return "Request ID: "+ self.request_id + "Request Name: "+ self.request_name+ \ "Request Contact: "+ self.request_contact+ "Request Blood Type: "+ self.request_blood_type+\ "Request Quantity: "+ self.request_quantity+ "Request Status: "+ self.request_status + \ "Date of Request: "+ self.date_of_request+ "Date of Requirement: "+ self.date_of_requirement
问题排查及解决方法
1. 检查数据库迁移是否完整
外键关联需要数据库层面的约束支持,添加外键后必须执行迁移才能生效:
- 生成迁移文件:
python manage.py makemigrations - 应用迁移到数据库:
python manage.py migrate
如果之前修改过模型但没生成迁移,或者迁移未完全应用,都会导致数据库没有外键约束,触发Operational Error。
2. 修复__str__方法中的类型错误
多个模型的__str__方法存在整数/日期与字符串直接拼接的问题,比如Donor的donor_id是整数,直接和字符串拼接会触发类型异常,这可能导致Admin加载模型时出错(异常可能被掩盖为Operational Error)。
以Donor模型为例,修改__str__为:
def __str__(self): return f"Donor ID: {self.donor_id} Donor First Name: {self.donor_firstname} Donor Surname: {self.donor_surname} Donor Date of Birth: {self.donor_dob} Donor Gender: {self.donor_gender} Donor Contact Phone Number: {self.donor_contact} Donor Email: {self.donor_email} Donor medical History: {self.donor_medical_history}"
同理,Hospitals、BloodBanks、Storage的__str__都需要用f-string或者把数值转成字符串后再拼接。
3. 确认数据库引擎支持外键
如果使用MySQL,确保表引擎是InnoDB(MyISAM不支持外键);PostgreSQL默认引擎支持外键。可以通过数据库客户端查看表的引擎类型,必要时修改DATABASE配置中的ENGINE参数:
# settings.py DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'OPTIONS': { 'init_command': "SET sql_mode='STRICT_TRANS_TABLES', innodb_strict_mode=1", }, # 其他配置... } }
4. 检查数据一致性
如果数据库中已有数据,比如Storage表的hospital_id对应的记录在Hospitals表中不存在,会触发外键约束错误。需要清理脏数据,确保外键关联的记录都存在于关联表中。
内容的提问来源于stack exchange,提问作者Mithun
相关产品推荐
相关产品推荐

