Django ProgrammingError:在 Django 源代码中创建迁移后,关系已经存在?

Posted

技术标签:

【中文标题】Django ProgrammingError:在 Django 源代码中创建迁移后,关系已经存在?【英文标题】:Django ProgrammingError: relation already exists after a migration created in the Django source code? 【发布时间】:2018-10-06 08:38:33 【问题描述】:

我最近检查了一个项目的 master 分支,其中有一些模型更改尚未反映在迁移中:

(venv) Kurts-MacBook-Pro-2:lucy-web kurtpeek$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auditlog, auth, contenttypes, lucy_web, oauth2_provider, otp_static, otp_totp, sessions, two_factor
Running migrations:
  No migrations to apply.
  Your models have changes that are not yet reflected in a migration, and so won't be applied.
  Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.

按照说明,我运行makemigrations 来创建它们:

(venv) Kurts-MacBook-Pro-2:lucy-web kurtpeek$ python manage.py makemigrations
Migrations for 'auth':
  venv/lib/python3.6/site-packages/django/contrib/auth/migrations/0009_auto_20180425_1129.py
    - Alter field email on user
Migrations for 'lucy_web':
  lucy_web/migrations/0146_auto_20180425_1129.py
    - Alter field description on sessiontype
    - Alter field short_description on sessiontype

有趣的是,0009_auto_20180425_1129.py 迁移是在包含 Django 源代码(版本 1.11.9)的 venv 中创建的,我相信我们团队中的任何人都没有更改。这是这次迁移:

# -*- coding: utf-8 -*-
# Generated by Django 1.11.9 on 2018-04-25 18:29
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('auth', '0008_alter_user_username_max_length'),
    ]

    operations = [
        migrations.AlterField(
            model_name='user',
            name='email',
            field=models.EmailField(blank=True, max_length=254, unique=True, verbose_name='email address'),
        ),
    ]

看起来“足够无辜”,但是当我尝试迁移时,我得到以下ProgrammingError

(venv) Kurts-MacBook-Pro-2:lucy-web kurtpeek$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auditlog, auth, contenttypes, lucy_web, oauth2_provider, otp_static, otp_totp, sessions, two_factor
Running migrations:
  Applying auth.0009_auto_20180425_1129...Traceback (most recent call last):
  File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
psycopg2.ProgrammingError: relation "auth_user_email_1c89df09_uniq" already exists

django.db.utils.ProgrammingError: relation already exists 的一些答案似乎非常激烈,例如删除所有迁移或使用命令选项 --fake,但没有提供从根本上导致错误的解释。

知道如何解决这个错误吗?

【问题讨论】:

makemigrations 不应在 django.contrib.auth 应用程序中生成迁移。您应该尝试找出项目中的哪些代码导致创建此迁移。 【参考方案1】:

事实证明auth_user_email_1c89df09_uniq 关系实际上是一个约束(所以不是数据)。我通过简单地在 pgAdmin 中删除/删除此约束来设法迁移,对于auth_user_email_1c89df09_like 索引(之后弹出ProgrammingError)也是如此。

在此之后,我能够迁移:

(venv) Kurts-MacBook-Pro-2:lucy-web kurtpeek$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auditlog, auth, contenttypes, lucy_web, oauth2_provider, otp_static, otp_totp, sessions, two_factor
Running migrations:
  Applying auth.0009_auto_20180425_1129... OK
  Applying lucy_web.0146_auto_20180425_1129... OK

并且约束和索引已经放回auth_user表中:

【讨论】:

我也遇到了同样的问题,上次迁移成功了,但是突然出现这个错误,这个解决方案对数据有副作用吗?【参考方案2】:

试试这个,这会奏效:

注意:此字段中的所有数据都将丢失

运行最后一次迁移后,您拥有此文件 0009_auto_20180425_1129.py 正在等待迁移...如果您没有此文件,请重新运行 makemigrations 以使最后一个迁移文件等待 migrate

浏览那个文件,在你的情况下是0009_auto_20180425_1129.py,然后在里面 operations

我想你在 db 中没有任何数据

添加这些行:

migrations.RemoveField(
    model_name='user',
    name='email',
),
migrations.AddField(
    model_name='user',
    name='email',
    field=models.EmailField(blank=True, max_length=254, unique=True, verbose_name='email address'   
),

随意评论你得到了什么

【讨论】:

如果数据库中有任何重要数据,那么删除电子邮件字段并重新创建它不是一个好的建议。 据我所知,db中没有数据【参考方案3】:

就我而言,罪魁祸首是

User._meta.get_field("email")._unique = True

.py 文件中的某个位置。

删除该行后,makemigrations 停止在auth 文件夹中创建迁移文件,同时保留该行创建的uniq 约束。

【讨论】:

【参考方案4】:

根据 Lemayzeur 的建议,我在搜索了几个解决方案后,找到了一个不会丢失数据的解决方案:

STORED_DATA = 

def store_data(apps, schema_editor):
    User = apps.get_model('users', 'User')
    for user in User.objects.all():
        STORED_DATA[user.id] = user.email


def restore_data(apps, schema_editor):
    User = apps.get_model('users', 'User')
    for key, value in STORED_DATA.items():
        user = User.objects.get(id=key)
        user.email = value
        user.save()


class Migration(migrations.Migration):

    dependencies = [
        ...
    ]

    operations = [
        migrations.RunPython(store_data, restore_data),
        migrations.RemoveField(
            model_name='user',
            name='email',
        ),
        migrations.AddField(
            model_name='user',
            name='email',
            field=models.EmailField(
                blank=True,
                max_length=254,
                unique=True,
                verbose_name='email address',
            )
        ),
        migrations.RunPython(restore_data, store_data),
    ]

【讨论】:

以上是关于Django ProgrammingError:在 Django 源代码中创建迁移后,关系已经存在?的主要内容,如果未能解决你的问题,请参考以下文章

django.db.utils.ProgrammingError:表不存在

Django 测试 django.db.utils.ProgrammingError:(1146,“表 'DB.Table' 不存在”)

Django ProgrammingError:在 Django 源代码中创建迁移后,关系已经存在?

django.db.utils.ProgrammingError: 1146 的解决办法

解决“django.db.utils.ProgrammingError: 关系 django_migrations 的权限被拒绝”的步骤

django.db.utils.ProgrammingError:类型“int4range”不存在