忽略所有当前迁移并从当前模型状态开始而不删除迁移 files/history
Ignore all current migrations and start from current model state WITHOUT deleting the migration files/history
前言:使用非常大、非常旧的 Django 应用程序从 Oracle 迁移到 Postgres。
需要能够从我的模型的当前状态开始一个新的数据库,忽略 5 年以上的迁移,但不删除任何迁移文件。
我的解决方案是使用 Django,让它为我完成所有工作。
- 使用
default
中的所有变量使用假连接创建数据库。将 USER
更改为 postgres
,因为它是唯一可以访问数据库的用户 postgres
,并且由于之前的原因,NAME
更改为 postgres
。 ('main' 我叫它,它只用了一次,创建数据库)
- 弄清楚我的应用程序的路径是什么,遍历我的所有应用程序,如果应用程序在我的路径本地,则在应用程序路径中创建一个临时
migrations_<random>
文件夹,然后 [=18= 】 里面那个。最后,更新 MIGRATION_MODULES
告诉 django 在哪里可以找到所述应用程序的迁移文件夹。
- 呼叫
./manage.py makemigrations
,制作它们。 (记住这将使用来自 MIGRATION_MODULES
的迁移)
- 调用
./manage.py migrate
,并应用它们。 (记住这将使用来自 MIGRATION_MODULES
的迁移)
- 删除
django_migrations
table 中的所有迁移
- (清理)将
MIGRATION_MODULES
设置为原始状态,删除所有应用程序中创建的所有 migrations_<random>
文件夹。
- 调用
./manage.py migrate --fake
以“应用”您在项目中的所有现有迁移
尽情享受吧!
import inspect
import os
import tempfile
from django.apps import apps
from django.db import connection, connections
from contextlib import ExitStack
from django.conf import settings
from django.core.management import call_command
from django.db.migrations.recorder import MigrationRecorder
def create_database_and_models():
settings.DATABASES['main'] = settings.DATABASES['default'].copy()
settings.DATABASES['main']['USER'] = 'postgres'
settings.DATABASES['main']['NAME'] = 'postgres'
with connections['main'].cursor() as cursor:
database_name = settings.DATABASES['default']['NAME']
cursor.execute('CREATE DATABASE %s' % (database_name,))
print('Create db %s' % (database_name))
original_migration_modules = settings.MIGRATION_MODULES.copy()
project_path = os.path.abspath(os.curdir)
with ExitStack() as stack:
# Find each app that belongs to the user and are not in the site-packages. Create a fake temporary
# directory inside each app that will tell django we don't have any migrations at all.
for app_config in apps.get_app_configs():
module = app_config.module
app_path = os.path.dirname(os.path.abspath(inspect.getsourcefile(module)))
if app_path.startswith(project_path):
temp_dir = stack.enter_context(tempfile.TemporaryDirectory(prefix='migrations_', dir=app_path))
# Need to make this directory a proper python module otherwise django will refuse to recognize it
open(os.path.join(temp_dir, '__init__.py'), 'a').close()
settings.MIGRATION_MODULES[app_config.label] = '%s.%s' % (app_config.module.__name__,
os.path.basename(temp_dir))
# create brand new migrations in fake migration folders local to the app
call_command('makemigrations')
# call migrate as normal, this will just read those fake folders with migrations in them.
call_command('migrate')
# delete all the migrations applied, remember these where NOT the real migrations from the project
MigrationRecorder.Migration.objects.all().delete()
settings.MIGRATION_MODULES = original_migration_modules
# Fake all the current migrations in the project
call_command('migrate', fake=True)
我假设您想在 PostgreSQL 中创建数据库时“重新开始”,但如果需要,仍然能够在旧的 Oracle 数据库上使用(甚至更新)您的迁移。我可以想到 3 个选项来实现:
压缩迁移
在某种程度上,这可以由 Django 本身处理,但 Django 并不总是对所有事情都是正确的,它实际上会留下一些东西来手动修复它们,就像任何 RunPython
操作一样。
通过简单地调用 ./manage.py squashmigrations
,您可以要求 Django 为您之前拥有的所有旧迁移创建一个替代迁移。如果之前的任何迁移应用到您的数据库,Django 将忽略试图替换它们的压缩迁移并继续应用旧迁移,但是当应用 none 被压缩迁移替换的迁移时,Django将忽略旧的迁移并只应用压缩的迁移,将其视为实现相同数据库状态的更快方法。
手动为现有迁移创建替换(压缩迁移)
当您在 Django 中压缩迁移时,此解决方案理解了幕后实际发生的事情,它与压缩过程非常相似,但实现方式不同。
在后台,当您尝试压缩迁移时,Django 将创建一个迁移,其中仅包含所有先前迁移的所有步骤(如果可能,应用一些优化)并将添加一个特殊字段 replaces
在 Migration
class 里面,包含一个被压扁的旧迁移列表。
这意味着您可以创建自己的迁移来替换其他迁移列表。 这是迁移系统的高级用法,请谨慎使用!实现方法:
- 确保您当前的模型状态在 Django 迁移中得到完整体现。要检查,运行
./manage.py makemigrations YOUR_APP_NAME --dry-run
并确保 returns No changes detected in app 'YOUR_APP_NAME'
.
- 暂时将所有要替换的迁移移到迁移目录之外,这样 Django 就不知道它们的存在
- 运行
./manage.py migrate YOUR_APP_NAME
生成替换旧迁移的新迁移
- 在新创建的迁移中
Migration
class 的顶部添加 replaces = [...]
属性,将三个点替换为此迁移将替换的所有迁移的列表(要获取该列表,您可以预先创建一个压缩的迁移,只是为了从中复制 replaces
字段)
- 确保新的迁移名称不与任何旧名称冲突。您现在可以毫无问题地更改它的名称,即使这是初始迁移(迁移的名称并不重要,它们只需要在必要时被其他迁移正确引用,您甚至可以摆脱迁移编号)
- 将旧迁移移回您的迁移目录
- 通过调用 ./manage.py makemigrations YOUR_APP_NAME --dry-运行` 确保迁移仍然创建与以前相同的数据库状态。
请注意,对于此解决方案(对于自动创建的压缩迁移),您需要为每个一致的迁移文件序列(或图形)进行一次替换迁移。这意味着如果您想要替换从 0002 到 0008 的迁移,保留 0009 并替换 0010 到 0012,您将需要 2 个替换迁移,每个序列范围一个。迁移的“图表”也是如此(例如,如果您有两个 0004 迁移和一个合并迁移 0005)。
对于上述两个选项,当至少有一个旧迁移已应用于您的数据库时,Django 将使用旧迁移文件,如果应用了 none 个,则将使用新迁移文件。在 squash 迁移之后创建的每个迁移都将在这两种情况下使用。
为您的数据库保留单独的迁移列表。
也可以将两个迁移列表放在不同的目录中,从而将它们分开。 Django 允许您使用 MIGRATION_MODULES
设置覆盖每个应用程序的迁移文件的默认位置。请注意,迁移将完全分开,因此您需要手动使它们保持同步。在您的应用程序模型中进行一些更改时,您需要 运行 ./manage.py makemigrations
两次,在 运行 之间更改 MIGRATION_MODULES
设置,否则您将不得不从一个位置到另一个位置。我强烈建议不要使用此解决方案,因为它很容易搞乱迁移列表之间的同步,并且除了在从头开始重新创建 Oracle 数据库时出于某种原因需要旧进程的情况外,其他方法也没有任何好处(你仍然可以使用旧方法,但在这种情况下您必须手动调用旧迁移之一。
前言:使用非常大、非常旧的 Django 应用程序从 Oracle 迁移到 Postgres。
需要能够从我的模型的当前状态开始一个新的数据库,忽略 5 年以上的迁移,但不删除任何迁移文件。
我的解决方案是使用 Django,让它为我完成所有工作。
- 使用
default
中的所有变量使用假连接创建数据库。将USER
更改为postgres
,因为它是唯一可以访问数据库的用户postgres
,并且由于之前的原因,NAME
更改为postgres
。 ('main' 我叫它,它只用了一次,创建数据库) - 弄清楚我的应用程序的路径是什么,遍历我的所有应用程序,如果应用程序在我的路径本地,则在应用程序路径中创建一个临时
migrations_<random>
文件夹,然后 [=18= 】 里面那个。最后,更新MIGRATION_MODULES
告诉 django 在哪里可以找到所述应用程序的迁移文件夹。 - 呼叫
./manage.py makemigrations
,制作它们。 (记住这将使用来自MIGRATION_MODULES
的迁移) - 调用
./manage.py migrate
,并应用它们。 (记住这将使用来自MIGRATION_MODULES
的迁移) - 删除
django_migrations
table 中的所有迁移
- (清理)将
MIGRATION_MODULES
设置为原始状态,删除所有应用程序中创建的所有migrations_<random>
文件夹。 - 调用
./manage.py migrate --fake
以“应用”您在项目中的所有现有迁移
尽情享受吧!
import inspect
import os
import tempfile
from django.apps import apps
from django.db import connection, connections
from contextlib import ExitStack
from django.conf import settings
from django.core.management import call_command
from django.db.migrations.recorder import MigrationRecorder
def create_database_and_models():
settings.DATABASES['main'] = settings.DATABASES['default'].copy()
settings.DATABASES['main']['USER'] = 'postgres'
settings.DATABASES['main']['NAME'] = 'postgres'
with connections['main'].cursor() as cursor:
database_name = settings.DATABASES['default']['NAME']
cursor.execute('CREATE DATABASE %s' % (database_name,))
print('Create db %s' % (database_name))
original_migration_modules = settings.MIGRATION_MODULES.copy()
project_path = os.path.abspath(os.curdir)
with ExitStack() as stack:
# Find each app that belongs to the user and are not in the site-packages. Create a fake temporary
# directory inside each app that will tell django we don't have any migrations at all.
for app_config in apps.get_app_configs():
module = app_config.module
app_path = os.path.dirname(os.path.abspath(inspect.getsourcefile(module)))
if app_path.startswith(project_path):
temp_dir = stack.enter_context(tempfile.TemporaryDirectory(prefix='migrations_', dir=app_path))
# Need to make this directory a proper python module otherwise django will refuse to recognize it
open(os.path.join(temp_dir, '__init__.py'), 'a').close()
settings.MIGRATION_MODULES[app_config.label] = '%s.%s' % (app_config.module.__name__,
os.path.basename(temp_dir))
# create brand new migrations in fake migration folders local to the app
call_command('makemigrations')
# call migrate as normal, this will just read those fake folders with migrations in them.
call_command('migrate')
# delete all the migrations applied, remember these where NOT the real migrations from the project
MigrationRecorder.Migration.objects.all().delete()
settings.MIGRATION_MODULES = original_migration_modules
# Fake all the current migrations in the project
call_command('migrate', fake=True)
我假设您想在 PostgreSQL 中创建数据库时“重新开始”,但如果需要,仍然能够在旧的 Oracle 数据库上使用(甚至更新)您的迁移。我可以想到 3 个选项来实现:
压缩迁移
在某种程度上,这可以由 Django 本身处理,但 Django 并不总是对所有事情都是正确的,它实际上会留下一些东西来手动修复它们,就像任何 RunPython
操作一样。
通过简单地调用 ./manage.py squashmigrations
,您可以要求 Django 为您之前拥有的所有旧迁移创建一个替代迁移。如果之前的任何迁移应用到您的数据库,Django 将忽略试图替换它们的压缩迁移并继续应用旧迁移,但是当应用 none 被压缩迁移替换的迁移时,Django将忽略旧的迁移并只应用压缩的迁移,将其视为实现相同数据库状态的更快方法。
手动为现有迁移创建替换(压缩迁移)
当您在 Django 中压缩迁移时,此解决方案理解了幕后实际发生的事情,它与压缩过程非常相似,但实现方式不同。
在后台,当您尝试压缩迁移时,Django 将创建一个迁移,其中仅包含所有先前迁移的所有步骤(如果可能,应用一些优化)并将添加一个特殊字段 replaces
在 Migration
class 里面,包含一个被压扁的旧迁移列表。
这意味着您可以创建自己的迁移来替换其他迁移列表。 这是迁移系统的高级用法,请谨慎使用!实现方法:
- 确保您当前的模型状态在 Django 迁移中得到完整体现。要检查,运行
./manage.py makemigrations YOUR_APP_NAME --dry-run
并确保 returnsNo changes detected in app 'YOUR_APP_NAME'
. - 暂时将所有要替换的迁移移到迁移目录之外,这样 Django 就不知道它们的存在
- 运行
./manage.py migrate YOUR_APP_NAME
生成替换旧迁移的新迁移 - 在新创建的迁移中
Migration
class 的顶部添加replaces = [...]
属性,将三个点替换为此迁移将替换的所有迁移的列表(要获取该列表,您可以预先创建一个压缩的迁移,只是为了从中复制replaces
字段) - 确保新的迁移名称不与任何旧名称冲突。您现在可以毫无问题地更改它的名称,即使这是初始迁移(迁移的名称并不重要,它们只需要在必要时被其他迁移正确引用,您甚至可以摆脱迁移编号)
- 将旧迁移移回您的迁移目录
- 通过调用 ./manage.py makemigrations YOUR_APP_NAME --dry-运行` 确保迁移仍然创建与以前相同的数据库状态。
请注意,对于此解决方案(对于自动创建的压缩迁移),您需要为每个一致的迁移文件序列(或图形)进行一次替换迁移。这意味着如果您想要替换从 0002 到 0008 的迁移,保留 0009 并替换 0010 到 0012,您将需要 2 个替换迁移,每个序列范围一个。迁移的“图表”也是如此(例如,如果您有两个 0004 迁移和一个合并迁移 0005)。
对于上述两个选项,当至少有一个旧迁移已应用于您的数据库时,Django 将使用旧迁移文件,如果应用了 none 个,则将使用新迁移文件。在 squash 迁移之后创建的每个迁移都将在这两种情况下使用。
为您的数据库保留单独的迁移列表。
也可以将两个迁移列表放在不同的目录中,从而将它们分开。 Django 允许您使用 MIGRATION_MODULES
设置覆盖每个应用程序的迁移文件的默认位置。请注意,迁移将完全分开,因此您需要手动使它们保持同步。在您的应用程序模型中进行一些更改时,您需要 运行 ./manage.py makemigrations
两次,在 运行 之间更改 MIGRATION_MODULES
设置,否则您将不得不从一个位置到另一个位置。我强烈建议不要使用此解决方案,因为它很容易搞乱迁移列表之间的同步,并且除了在从头开始重新创建 Oracle 数据库时出于某种原因需要旧进程的情况外,其他方法也没有任何好处(你仍然可以使用旧方法,但在这种情况下您必须手动调用旧迁移之一。