扩展 Django 模型

问题描述 投票:0回答:1

我的项目中有两个应用程序,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 文件中是合理的。

python django django-models webserver database-schema
1个回答
1
投票

您正在使用

OneToOneField

category = models.OneToOneField('app2.Category',

这将为该字段创建一个唯一的数据库索引,因为每个值都应该是一个不同的记录。这是您获得的索引

IntegrityError

至于为什么,我怀疑是迁移的原因。您可能想要做的是这里的 3 部分迁移:

  1. 添加不带
    default
    值的字段并允许为 null
  2. 使用自定义迁移手动填充字段
  3. 正确填充字段后,添加
    default
    设置以获取新值。
© www.soinside.com 2019 - 2024. All rights reserved.