我的项目中有两个应用程序,app1 和 app2。
在 app1 中,我有两个模型,称为任务和作业。
我想为这两个模型添加新功能,我想为它们添加类别。
但它比一个新字段更复杂,app2 主要负责类别的估计,所以我在 app2 中创建了新模型,如下所示:
class Category(models.Model):
CAT1 = 1
CAT2 = 2
OTHER = 6
UNKNOWN = 7
CATEGORY_CHOICES = (
(CAT1, "Cat1"),
(CAT2, "Cat2"),
(OTHER, "Other"),
(UNKNOWN, "Unknown"),
)
auto_category = models.PositiveSmallIntegerField(choices=CATEGORY_CHOICES, default=UNKNOWN)
user_category = models.PositiveSmallIntegerField(choices=CATEGORY_CHOICES, default=UNKNOWN)
modify_timestamp = models.DateTimeField(blank=True, null=True)
modified_by = models.ForeignKey("app1.User", on_delete=models.PROTECT, related_name='category_creator', blank=True, null=True)
我还希望每个任务和作业对象在创建后立即有一个默认类别,这样我就不必在前端处理 None 值,“未知”值应该是默认值,所以我在以下函数中创建了app2/utils.py:
def create_default_category():
new_category = Category.objects.create()
return new_category.pk
然后我将 OneToOneField 关系添加到 app1 中的任务和作业模型以及默认值:
class Task(models.Model):
# Some already existing fields
category = models.OneToOneField('app2.Category', on_delete=models.CASCADE, related_name='task_category', default=create_default_category)
class Job(models.Model):
# Some already existing fields
category = models.OneToOneField('app2.Category', on_delete=models.CASCADE, related_name='job_category', default=create_default_category)
但是以上所有似乎都没有按照我的预期工作。
当我尝试运行 app1 的迁移时,出现错误:
(venv) C:\project>python manage.py migrate app1
Operations to perform:
Apply all migrations: app1
Running migrations:
Applying app1.0003_auto_20230828_1538...
Traceback (most recent call last):
File "C:\project\venv\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
psycopg2.errors.UniqueViolation: could not create unique index "app1_task_category_id_key"
DETAIL: Key (category_id)=(1) is duplicated.
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "manage.py", line 21, in <module>
main()
File "manage.py", line 17, in main
execute_from_command_line(sys.argv)
File "C:\project\venv\lib\site-packages\django\core\management\__init__.py", line 381, in execute_from_command_line
utility.execute()
File "C:\project\venv\lib\site-packages\django\core\management\__init__.py", line 375, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "C:\project\venv\lib\site-packages\django\core\management\base.py", line 323, in run_from_argv
self.execute(*args, **cmd_options)
File "C:\project\venv\lib\site-packages\django\core\management\base.py", line 364, in execute
output = self.handle(*args, **options)
File "C:\project\venv\lib\site-packages\django\core\management\base.py", line 83, in wrapped
res = handle_func(*args, **kwargs)
File "C:\project\venv\lib\site-packages\django\core\management\commands\migrate.py", line 232, in handle
post_migrate_state = executor.migrate(
File "C:\project\venv\lib\site-packages\django\db\migrations\executor.py", line 117, in migrate
state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial)
File "C:\project\venv\lib\site-packages\django\db\migrations\executor.py", line 147, in _migrate_all_forwards
state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)
File "C:\project\venv\lib\site-packages\django\db\migrations\executor.py", line 245, in apply_migration
state = migration.apply(state, schema_editor)
File "C:\project\venv\lib\site-packages\django\db\migrations\migration.py", line 124, in apply
operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
File "C:\project\venv\lib\site-packages\django\db\migrations\operations\fields.py", line 110, in database_forwards
schema_editor.add_field(
File "C:\project\venv\lib\site-packages\django\db\backends\base\schema.py", line 447, in add_field
self.execute(sql, params)
File "C:\project\venv\lib\site-packages\django\db\backends\base\schema.py", line 137, in execute
cursor.execute(sql, params)
File "C:\project\venv\lib\site-packages\django\db\backends\utils.py", line 99, in execute
return super().execute(sql, params)
File "C:\project\venv\lib\site-packages\django\db\backends\utils.py", line 67, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "C:\project\venv\lib\site-packages\django\db\backends\utils.py", line 76, in _execute_with_wrappers
return executor(sql, params, many, context)
File "C:\project\venv\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "C:\project\venv\lib\site-packages\django\db\utils.py", line 89, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "C:\project\venv\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
django.db.utils.IntegrityError: could not create unique index "app1_task_category_id_key"
DETAIL: Key (category_id)=(1) is duplicated.
我的数据库中有很多已经存在的任务和作业模型对象,数据库中没有类别模型对象,该表实际上是空的。
有谁知道什么可能会导致这个问题?或者此类问题的正确解决方案是什么?
我已经运行了 app2 的迁移,并且通常在数据库中创建表,我还可以在数据库中手动创建新的类别对象。
我尝试重置数据库中 app2.Category 表中的索引,但它没有改变任何内容。
我恢复并再次运行迁移 - 它也没有帮助。
我还知道我可以在 app1 中创建一个抽象模型类,然后在任务和作业模型中继承它 - 这是更好的解决方案吗?
我只是认为,由于 Category 与 app2 的联系更多,因此将代码放在 app2/models.py 文件中是合理的。
您正在使用
OneToOneField
category = models.OneToOneField('app2.Category',
这将为该字段创建一个唯一的数据库索引,因为每个值都应该是一个不同的记录。这是您获得的索引
IntegrityError
至于为什么,我怀疑是迁移的原因。您可能想要做的是这里的 3 部分迁移:
default
值的字段并允许为 nulldefault
设置以获取新值。