【问题标题】:Django-taggit migration fails at AlterUniqueTogetherDjango-taggit 迁移在 AlterUniqueTogether 失败
【发布时间】:2020-06-04 05:48:27
【问题描述】:

我正在尝试迁移使用 jazzband django-taggit 的 Django 应用程序 我得到的错误是:

django.db.utils.IntegrityError: could not create unique index "taggit_taggeditem_content_type_id_object_i_4bb97a8e_uniq"
DETAIL:  Key (content_type_id, object_id, tag_id)=(41, 596, 242) is duplicated.

有问题的迁移内容如下:

        migrations.AlterUniqueTogether(
            name="taggeditem", unique_together={("content_type", "object_id", "tag")}
        )

https://github.com/jazzband/django-taggit/blob/master/taggit/migrations/0003_taggeditem_add_unique_index.py#L12-L14

转换为以下 SQL:

ALTER TABLE "taggit_taggeditem" ADD CONSTRAINT "taggit_taggeditem_content_type_id_object_i_4bb97a8e_uniq" UNIQUE ("content_type_id", "object_id", "tag_id");
COMMIT;

检查有问题的表我确实得到:

# SELECT * FROM public.taggit_taggeditem WHERE tag_id=242 ORDER BY object_id;
  id  | tag_id | object_id | content_type_id 
------+--------+-----------+-----------------
  691 |    242 |       356 |              41
 2904 |    242 |       356 |              41
  680 |    242 |       486 |              41
 2893 |    242 |       486 |              41
  683 |    242 |       596 |              41
 2896 |    242 |       596 |              41

解决django.db.utils.IntegrityError 错误并成功完成迁移的建议方法是什么?我认为 object_id 486 和 356(+ 更多)也会发生同样的情况。

【问题讨论】:

  • 您应该先过滤掉重复项。
  • 所以例如我只会保留 id 691 并删除 2904,因为它们具有相同的 tag_id、object_id 和 content_type_id,对吧?

标签: django django-taggit


【解决方案1】:

在迁移模型之前,您应该创建一个data migration。因此,您最好先删除迁移文件,然后使用以下命令进行数据迁移:

python3 manage.py makemigrations <b>--empty</b> <i>app_name</i>

在此迁移中可能如下所示:

from django.db import migrations
from django.db.models import Exists, OuterRef

def remove_duplicates(apps, schema_editor):
    Model = apps.get_model('app_name', 'ModelName')
    Model.objects.annotate(
        has_dup=Exists(
            Model.objects.filter(
                pk__lt=OuterRef('pk'),
                content_type_id=OuterRef('content_type_id'),
                object_id=OuterRef('object_id'),
                tag_id=OuterRef('tag_id'),
            )
        )
    ).filter(has_dup=True).delete()

class Migration(migrations.Migration):

    dependencies = [
        ('app_name', '1234_some_migration'),
    ]

    operations = [
        migrations.RunPython(remove_duplicates),
    ]

您将 app_nameModelName 分别替换为应用名称和模型的位置(并检查它是否依赖于之前的迁移文件)。

因此,我们在这里寻找包含重复数据且主键小于当前主键的Models。我们会删除此类记录。

接下来,您再次为应用程序进行迁移:

python3 manage.py makemigrations <i>app_name</i>

我会(强烈)建议在运行之前先备份数据库,因为数据迁移总是可能存在复杂的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-05-29
    • 2017-03-10
    • 1970-01-01
    • 1970-01-01
    • 2015-06-26
    • 2012-11-21
    • 2020-04-24
    相关资源
    最近更新 更多