在给网站数据库优化的过程中,需要给几张表增加外键限制,方便管理员界面的联表操作。
调研发现,网上提供的几种models的配置都不能让我的插入程序成功运行,在经过尝试之后总结出了一个终极解决方案。
我的环境:Django==2.1.7,Python==3.6.1
我建表一般先过Navicat这种可视化软件来生成mysql数据库,所以我们直接在建表之后使用下面语句直接生成。
python manage.py inspectdb > app/models.py
// 'app' 是我项目名称
生成的models.py部分代码:
class InfoUpload(models.Model):
# 子表
upload_id = models.AutoField(primary_key=True)
contact_email = models.ForeignKey('InfoUser', model.DO_NOTHING, db_column='contact_email')
class Meta:
managed = False
db_table = 'info_upload'
class InfoUser(models.Model):
# 主表
user_id = models.AutoField(primary_key=True)
contact_email = models.CharField(max_length=255)
class Meta:
managed = False
db_table = 'info_user'
unique_together = (('user_id', 'contact_email'),)
可以看到在子表(有外键的表)中,外键contact_email生成了三个设置字段,'InfoUser'表示参照的表, model.DO_NOTHING是插入时的动作(感觉这个字段是个bug,我们一会要用级联来替代它), db_column='contact_email'表中列名。
在主表中,contact_email只有一个最大长度字段。
在插入之后我们遇到了以下的报错:
// 报错 1
// 说我没有设置级联
django.db.utils.IntegrityError: (1452, 'Cannot add or update a child row: a foreign key constraint fails (`corpusdatabase`.`info_upload`, CONSTRAINT `upload_email` FOREIGN KEY
(`contact_email`) REFERENCES `info_user` (`contact_email`) ON DELETE CASCADE ON UPDATE CASCADE)')
// 报错 2
// 说我没有设置主键唯一
ERRORS:
pageContent.InfoUpload.contact_email: (fields.E311) 'InfoUser.contact_email' must set unique=True because it is referenced by a foreign key.
推测原因是inspectdb指令生成的models并不是完整状态的,对于我这种懒人并不是很友好,还需要自己再加入一些设置。
不断试错之后得到最终版本如下:
class InfoUpload(models.Model):
upload_id = models.AutoField(primary_key=True)
upload_date = models.DateTimeField(blank=True, null=True)
contact_email = models.ForeignKey('InfoUser', on_delete='CASECADE', db_column='contact_email', to_field='contact_email', related_name="upload_name")
# models.DO+_NOTHING 改为 on_delete='CASECADE', 设置级联操作
# 加入 to_field='contact_email',指出参考表中哪一列
# 加入 related_name="upload_name", 方便之后的联表查询
class Meta:
managed = False
db_table = 'info_upload'
class InfoUser(models.Model):
user_id = models.AutoField(primary_key=True)
contact_email = models.CharField(max_length=255, unique=True)
# 增加字段unique=True,设置这个键的值是唯一的
class Meta:
managed = False
db_table = 'info_user'
unique_together = (('user_id', 'contact_email'),)
应该就能解决了。
综上,这个故事告诉我们,不能太依赖inspectdb这条语句,对于生成的models还需要进一步的修改。