首页 > 解决方案 > 无法插入数据,Django 主键没有默认值

问题描述

在 Django 3.0 中,我有一组模型,包括一个抽象BaseData、一个Data扩展它的模型和一个外键到的基础数据FKData模型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

basedata_ptr_id是 Django 生成的主键字段,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)

该错误听起来好像没有设置为AUTO INCREMENT正常主键的方式?

如果我尝试使用 Django shell 填充Data表格,我会得到相同的错误,但堆栈跟踪可能会更大:

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")

出了什么问题,我该如何解决?

编辑:我决定擦除我的数据库并停止所有开发,同时我花了几周的时间重建它,这一切都是因为我忽略了一行,因为我正在使用“有期限的完美主义者框架”。

然而,这个问题值得更好的回答。根据评论,我怀疑接近答案的最佳方法如下:

考虑两种情况。

方案 A

  1. 我定义BaseData为一个抽象类
class BaseData(models.Model):
    class Meta:
        abstract = True
  1. 我跑来makemigrations创建一个迁移文件

方案 B

  1. 我定义BaseData为非抽象类
class BaseData(models.Model):
    class Meta:
        pass
  1. 我跑来makemigrations创建一个迁移文件。
  2. BaseData变成了一个抽象类
class BaseData(models.Model):
    class Meta:
        abstract = True
  1. 我跑来makemigrations创建第二个迁移文件。

原则上,场景 B 的两个迁移的组合应该完全等于场景 A 的迁移。在实践中,有些不同。回答这个问题的第一步是确定两种方案之间的迁移指令有何不同。然后,我们可以制定一个手动编辑迁移文件的流程,在不擦除数据库的情况下解决问题,以供以后遇到同样问题的人使用。

标签: django

解决方案


推荐阅读