Django Admin添加数据报错NOT NULL约束失败,如何定位需修改属性?
搞定Django多表继承的NOT NULL约束错误
嘿,我来帮你解决这个头疼的问题!首先得搞懂为啥会出现NOT NULL constraint failed: HVAC_chiller.basicinfo_ptr_id这个错误——这其实是Django多表继承的机制在搞事情:
当你用VRV(BasicInfo)、Chiller(VRV)这种多表继承的时候,Django会自动给每个子模型生成一个指向父模型的外键字段,格式是[父模型小写名]_ptr_id。比如VRV表会有basicinfo_ptr_id(指向BasicInfo),Chiller表会有vrv_ptr_id(指向VRV)。你看到的错误,大概率是下面两种情况之一:
可能的原因&解决办法
1. 旧模型修改留下的冗余字段
如果你之前改过模型结构(比如一开始Chiller直接继承BasicInfo,后来改成继承VRV),那数据库表或者迁移记录里可能残留了basicinfo_ptr_id这个字段,而且它被设置成了非空,导致插入数据时出错。
解决步骤:
- 先去数据库里查一下
HVAC_chiller表,确认是不是真的有basicinfo_ptr_id这个字段。 - 如果有,先自动生成迁移文件清理它:
python manage.py makemigrations python manage.py migrate - 如果自动生成的迁移没识别到这个字段,就手动写一个迁移文件删掉它:
写完后再执行from django.db import migrations class Migration(migrations.Migration): dependencies = [ ('HVAC', '你上一次的迁移文件名'), ] operations = [ migrations.RemoveField( model_name='chiller', name='basicinfo_ptr', ), ]python manage.py migrate就好。
2. Admin页面没处理父模型的必填字段
你的BasicInfo里的title字段是必填的(没有加blank=True或null=True),如果在Admin里添加Chiller的时候没填这个字段,Django就没办法自动创建对应的BasicInfo和VRV实例,自然就会触发ptr_id的非空约束错误。
解决办法:
- 如果你自定义了
ChillerAdmin,一定要把父模型的必填字段加进去,比如:from django.contrib import admin from .models import Chiller class ChillerAdmin(admin.ModelAdmin): fieldsets = ( ('基础信息', { 'fields': ('title', 'manufacturer', 'used_project', 'cost', 'url', 'comment') }), ('VRV参数', { 'fields': ('InletT', 'OutletT', 'Capacity', 'COP') }), ('冷水机组参数', { 'fields': ('Condenser', 'CHWFlowRate', 'CWFlowRate', 'minPLR', 'maxPLR', 'optimumPLR', 'minUnloadRatio', 'CapacityFunction', 'EIRofTemp', 'EIRofPLR') }), ) admin.site.register(Chiller, ChillerAdmin) - 如果没自定义Admin,那Django默认会显示所有字段,但你一定要记得在添加
Chiller时填写title这个必填项,不然肯定会报错。
额外小技巧
如果你不需要单独查询BasicInfo或VRV的实例,其实可以把BasicInfo改成抽象基类,这样就不会生成单独的父模型表,也就不会有ptr字段的问题了:
class BasicInfo(models.Model): title = models.CharField(max_length=100) manufacturer = models.CharField(max_length=50, blank = True) used_project = models.CharField(max_length=50, null=True,blank=True) cost=models.IntegerField(null = True, blank = True) url=models.URLField(null=True,blank=True) comment=models.TextField(null=True,blank=True) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.title class Meta: abstract = True # 加上这一行,变成抽象基类
内容的提问来源于stack exchange,提问作者Katsuya Obara
相关产品推荐
相关产品推荐

