Django FK 约束投诉,但对象在那里。我想念啥?

Posted

技术标签:

【中文标题】Django FK 约束投诉,但对象在那里。我想念啥?【英文标题】:Dajango FK constraint complaint, but object is there. What do I miss?Django FK 约束投诉,但对象在那里。我想念什么? 【发布时间】:2022-01-05 13:51:24 【问题描述】:

我正在使用我的第一个 Django 应用程序,请多多包涵……

它基于一些带有现有 PostgreSQL 数据库的遗留脚本。因此,某些表的命名不遵循 Django 命名模式。

目前我正在重塑我的数据以遵循健全的数据建模原则。执行其中一个步骤的迁移给了我一个我不明白的外键异常:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/base.py", line 242, in _commit
    return self.connection.commit()
psycopg2.errors.ForeignKeyViolation: insert or update on table "design_shopify_category" violates foreign key constraint "design_shopify_categ_shopifycollection_id_9f310876_fk_designs_s"
DETAIL:  Key (shopifycollection_id)=([<ShopifyCollection: ShopifyCollection object (gid://shopify/Collection/263596736569)>]) is not present in table "designs_shopifycollection".


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/local/lib/python3.9/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/root/.vscode-server/extensions/ms-python.python-2021.11.1422169775/pythonFiles/lib/python/debugpy/__main__.py", line 45, in <module>
    cli.main()
  File "/root/.vscode-server/extensions/ms-python.python-2021.11.1422169775/pythonFiles/lib/python/debugpy/../debugpy/server/cli.py", line 444, in main
    run()
  File "/root/.vscode-server/extensions/ms-python.python-2021.11.1422169775/pythonFiles/lib/python/debugpy/../debugpy/server/cli.py", line 285, in run_file
    runpy.run_path(target_as_str, run_name=compat.force_str("__main__"))
  File "/usr/local/lib/python3.9/runpy.py", line 268, in run_path
    return _run_module_code(code, init_globals, run_name,
  File "/usr/local/lib/python3.9/runpy.py", line 97, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/usr/local/lib/python3.9/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/workspace/src/otaya_tool/manage.py", line 23, in <module>
    main()
  File "/workspace/src/otaya_tool/manage.py", line 19, in main
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.9/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.9/site-packages/django/core/management/__init__.py", line 413, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 354, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 398, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 89, in wrapped
    res = handle_func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/django/core/management/commands/migrate.py", line 244, in handle
    post_migrate_state = executor.migrate(
  File "/usr/local/lib/python3.9/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 "/usr/local/lib/python3.9/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 "/usr/local/lib/python3.9/site-packages/django/db/migrations/executor.py", line 230, in apply_migration
    migration_recorded = True
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/schema.py", line 120, in __exit__
    self.atomic.__exit__(exc_type, exc_value, traceback)
  File "/usr/local/lib/python3.9/site-packages/django/db/transaction.py", line 246, in __exit__
    connection.commit()
  File "/usr/local/lib/python3.9/site-packages/django/utils/asyncio.py", line 33, in inner
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/base.py", line 266, in commit
    self._commit()
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/base.py", line 242, in _commit
    return self.connection.commit()
  File "/usr/local/lib/python3.9/site-packages/django/db/utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/base.py", line 242, in _commit
    return self.connection.commit()
django.db.utils.IntegrityError: insert or update on table "design_shopify_category" violates foreign key constraint "design_shopify_categ_shopifycollection_id_9f310876_fk_designs_s"
DETAIL:  Key (shopifycollection_id)=([<ShopifyCollection: ShopifyCollection object (gid://shopify/Collection/263596736569)>]) is not present in table "designs_shopifycollection".

有两个模型,与错误相关:

class ShopifyCollection(models.Model):
    graph_ql_id = models.CharField(max_length=250, primary_key=True,
                                   validators=[validate_sy_graph_ql_id])
    handle = models.CharField(max_length=250, blank=False, null=False,
                              validators=[validate_slug])
    title = models.CharField(max_length=250, blank=False, null=False)

def pk_uuid_gen() -> str:
    return str(uuid4())

class Design(models.Model):
    uuid = models.CharField(primary_key=True, unique=True, default=pk_uuid_gen,
                            editable=False, max_length=36)
    niche = models.CharField(blank=False, null=True, max_length=50)
    id = models.CharField(blank=False, null=True, max_length=4)
    variant = models.CharField(blank=False, null=True, max_length=1)
    language = models.CharField(max_length=2, default=None)


    # the new many 2 many relation to be filled by the migration.
    shopify_category = models.ManyToManyField(ShopifyCollection, blank=True)

    # the legacy field to get the values from...
    shopify_category_tmp = models.JSONField(blank=True, default=empty_development_default)

    class Meta:
        db_table = 'design'
        unique_together = ((niche', 'id', 'variant'),)

这是迁移代码:

# Generated by Django 3.2.9 on 2021-11-27 18:00

from django.db import migrations
from django.core.exceptions import ObjectDoesNotExist
from designs.models import Design, ShopifyCollection


def convert_shopify_category_json_objects_to_m2m(apps, schema_editor):

    # this doesn't give an error. It's the exact same key the FK exception 
    # complains about. It's also the first object it want's to add.

    x = ShopifyCollection.objects.get(pk='gid://shopify/Collection/263596736569')

    for design in Design.objects.all().iterator():
        if len(design.shopify_category_tmp) == 0:
            continue

        # the legacy field holds a list of strings like: ["archery", "outdoor"]
        for category in design.shopify_category_tmp:
            if len(category) == 0:
                continue

            sy_col_handles = [
                f'niche-de-category',
                f'featured-de-category']
            sy_collections = []
            for handle in sy_col_handles:
                try:
                    sy_collections.append(ShopifyCollection.objects.get(handle=handle))

                except ObjectDoesNotExist:
                    pass

            if len(sy_collections) < 1:
                raise ValueError('There are no Shopify Collections for the handles ' +
                                 f'sy_col_handles. We need at least one of them.')

            design.shopify_category.add(sy_collections)
            design.save()


class Migration(migrations.Migration):

    dependencies = [
        ('designs', '0031_shopify_categories_link_them'),
    ]

    operations = [
        migrations.RunPython(convert_shopify_category_json_objects_to_m2m),
    ]

designs_shopifycollection 在迁移的数据库事务开始之前保存gid://shopify/Collection/263596736569 行,异常抱怨丢失。

x = ShopifyCollection.objects.get(pk='gid://shopify/Collection/263596736569') 行也证明该表包含迁移的 db 事务中请求的行。

该表还包含迁移后的“缺失”行。

我尝试通过 Django 框架代码调试问题,但过了一段时间就停止了。找不到比提交触发的异常更多...

还可以手动将行添加到 m2n 表中,没有任何问题,并显示为在 DesignFrom 视图中选择。

我的想法已经用完了。问题出在哪里?

【问题讨论】:

【参考方案1】:

您需要将项目作为单独的参数传递,所以:

#                  asterisk &downarrow;
design.shopify_category.add(*sy_collections)

【讨论】:

谢谢,就是这样。如果我没记错的话,* 正在展开它...... @andreas: 是的,如果sy_collections 具有例如三个元素abc,它用.add(a, b, c) 调用它。 如果能更好地验证允许卡在 .add() 方法中的内容,那就太好了。但我想在这里从无效中识别价值并不容易......

以上是关于Django FK 约束投诉,但对象在那里。我想念啥?的主要内容,如果未能解决你的问题,请参考以下文章

使用 MyISAM 的 Django 外键完整性

Django ORM:未继承子级的字段和值。对象重复。 (使用Django管理界面)

SonarQube 投诉要么删除这个无用的类对象实例化,要么使用它

当 JPA 插入继承的对象时强制执行 FK 约束

获取模型对象的 id,它是另一个模型对象 django 的 FK

在两个可为空的 FK 之间添加 SQL XOR 约束