在 Django 3.0 中,我有一组模型,其中包含一个抽象 BaseData
、一个扩展它的模型 Data
和一个 FKData
的底层数据模型 {{1}外键:
Data
当我尝试插入 # models.py
from django.db import models
class BaseData(models.Model):
class Meta:
abstract = True
class Data(BaseData):
data = models.ForeignKey(
FKData,
on_delete=models.CASCADE,
blank=True,
null=True,
default=None
)
class FKData(models.Model):
text = models.CharField(
help_text='underlying data'
)
时发生错误。例如,
Data
mysql> INSERT INTO appname_data (data_id) SELECT data_id FROM appname_othertable
ERROR 1364 (HY000): Field 'basedata_ptr_id' doesn't have a default value
是 Django 生成的主键字段,basedata_ptr_id
由于是 Data
的子类而具有:
BaseData
错误听起来好像没有像普通主键那样设置为 mysql> DESCRIBE appname_data;
+-----------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+---------+------+-----+---------+-------+
| basedata_ptr_id | int(11) | NO | PRI | NULL | |
| data_id | int(11) | YES | MUL | NULL | |
+-----------------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)
?
如果我尝试使用 Django shell 填充 AUTO INCREMENT
表,我会得到同样的错误,但有一个可能有用的更大的堆栈跟踪:
Data
出了什么问题,我该如何解决?
编辑:我决定擦除我的数据库并停止所有开发,同时我花了几个星期重建它,这都是因为我忽略了一行,因为我正在使用“具有截止日期的完美主义者的框架。”
尽管如此,这个问题值得更好的回答。根据评论,我怀疑接近答案的最佳方法如下:
考虑两种情况。
场景 A
Traceback (most recent call last):
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
return self.cursor.execute(sql, params)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/backends/mysql/base.py", line 74, in execute
return self.cursor.execute(query, args)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/MySQLdb/cursors.py", line 209, in execute
res = self._query(query)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/MySQLdb/cursors.py", line 315, in _query
db.query(q)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/MySQLdb/connections.py", line 239, in query
_mysql.connection.query(self, query)
MySQLdb._exceptions.IntegrityError: (1364, "Field 'basedata_ptr_id' doesn't have a default value")
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<console>", line 6, in <module>
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/models/base.py", line 746, in save
force_update=force_update, update_fields=update_fields)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/models/base.py", line 784, in save_base
force_update, using, update_fields,
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/models/base.py", line 887, in _save_table
results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/models/base.py", line 926, in _do_insert
using=using, raw=raw,
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/models/query.py", line 1204, in _insert
return query.get_compiler(using=using).execute_sql(returning_fields)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1391, in execute_sql
cursor.execute(sql, params)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/backends/utils.py", line 100, in execute
return super().execute(sql, params)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/backends/utils.py", line 68, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
return self.cursor.execute(sql, params)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
return self.cursor.execute(sql, params)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/django/db/backends/mysql/base.py", line 74, in execute
return self.cursor.execute(query, args)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/MySQLdb/cursors.py", line 209, in execute
res = self._query(query)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/MySQLdb/cursors.py", line 315, in _query
db.query(q)
File "/home/username/ProjectName/env/lib/python3.6/site-packages/MySQLdb/connections.py", line 239, in query
_mysql.connection.query(self, query)
django.db.utils.IntegrityError: (1364, "Field 'basedata_ptr_id' doesn't have a default value")
定义为抽象类BaseData
class BaseData(models.Model):
class Meta:
abstract = True
来创建迁移文件场景 B
makemigrations
定义为非抽象类BaseData
class BaseData(models.Model):
class Meta:
pass
来创建迁移文件。makemigrations
更改为抽象类BaseData
class BaseData(models.Model):
class Meta:
abstract = True
来创建第二个迁移文件。原则上,从场景 B 的两次迁移的组合应该与从场景 A 的迁移完全相等。在实践中,有些事情是不同的。回答这个问题的第一步是确定这两种方案之间的迁移指令有何不同。然后,我们可以制定一个手动编辑迁移文件的流程来解决问题,而无需擦除数据库,以供日后遇到同样问题的人使用。