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

在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 16:19:52