Python 如果 Django 中已经存在某些表,如何强制迁移到数据库?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/43880426/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to force migrations to a DB if some tables already exist in Django?
提问by Pablo Viacheslav
I have a Python/Django proyect. Due to some rolls back, and other mixed stuff we ended up in a kind of odd scenario.
我有一个 Python/Django 项目。由于一些回滚和其他混合因素,我们最终陷入了一种奇怪的情况。
The current scenario is like this:
现在的场景是这样的:
- DB has the correct tables
- DB can't be rolled back or dropped
Code is up to date
Migrations folder is behindthe DB by one or two migrations. (These migrations were applied from somewhere else and that "somewhere else" doesn't exist anymore)
I add and alter some models
- I run makemigrations
- New migrations are created, but it's a mix of new tables and some tables that already exist in the DB.
- If I run migrateit will complain that some of the tables that I'm trying to create already exist.
- DB 有正确的表
- DB 不能回滚或删除
代码是最新的
Migrations 文件夹位于DB后面一两个迁移。(这些迁移是从其他地方应用的,“其他地方”不再存在)
我添加并修改了一些模型
- 我运行makemigrations
- 新的迁移被创建,但它是新表和数据库中已经存在的一些表的混合。
- 如果我运行migrate它会抱怨我试图创建的一些表已经存在。
What I need:
我需要的:
To be able to run the migrations and kind of "ignore" the existing tables and apply the new ones. Or any alternative way to achieve this.
为了能够运行迁移并“忽略”现有表并应用新表。或任何替代方法来实现这一点。
Is that possible?
那可能吗?
回答by little_birdie
When you apply a migration, Django inserts a row in a table called django_migrations
. That's the only way Django knows which migrations have been applied already and which have not. So the rows in that table have to match the files in your migrations
directory. If you've lost the migration files after they were applied, or done anything else to get things out of sync, you'll have problems.. because the migration numbers in your database refer to different migration files than the ones in your project.
当您应用迁移时,Django 会在名为django_migrations
. 这是 Django 知道哪些迁移已经应用,哪些没有应用的唯一方式。因此,该表中的行必须与您migrations
目录中的文件相匹配。如果您在应用迁移文件后丢失了迁移文件,或者执行了任何其他操作以使事情不同步,您就会遇到问题.. 因为您数据库中的迁移编号指代的迁移文件与项目中的迁移文件不同。
So before you do anything else, you need to bring things back into sync by deleting the django_migrations
table rows for any migration files that you've lost somehow and can't get back. The table should contain rows for only those migrations that you do have and that were actually applied to the database correctly.
因此,在您执行任何其他操作之前,您需要通过删除django_migrations
以某种方式丢失且无法恢复的任何迁移文件的表行来使事情恢复同步。 该表应该只包含那些您确实拥有并且实际正确应用于数据库的迁移的行。
Now you need to deal with any changes in your database that Django Migrations doesn't know about.. and for that there are a few options:
现在,您需要处理 Django Migrations 不知道的数据库中的任何更改……为此,有几个选项:
If things worked out such that the database changes that were already applied to the database are in different migration files than the ones that weren't, then you can fix it by running your migrations one at a time using the --fake
option on any changes that are in reality already in the database. The fake option just writes the row to the django_migrations
table marking the migration as done. Only do this if the database does in fact already have all the changes contained in that migration file.
如果事情已经解决,已经应用到数据库的数据库更改与未应用的迁移文件位于不同的迁移文件中,那么您可以通过使用--fake
任何更改的选项一次运行一个迁移来修复它实际上已经在数据库中。fake 选项只是将行写入django_migrations
表,将迁移标记为已完成。仅当数据库实际上已经包含该迁移文件中的所有更改时才执行此操作。
And those migration files that contain only changes which have not been applied to the database, run without the --fake
option and Django will apply them. eg:
那些只包含尚未应用于数据库的更改的迁移文件,在没有--fake
选项的情况下运行,Django 将应用它们。例如:
# database already has it
manage.py migrate myapp 0003 --fake
# need it
manage.py migrate myapp 0004
# database already has it
manage.py migrate myapp 0005 --fake
If you have migration files where some but not all of the changes have been applied, then you have a bigger problem. In that case, there are several ways to go about it (choose ONLY ONE):
如果您的迁移文件中应用了部分而非全部更改,则问题更大。在这种情况下,有几种方法可以解决(仅选择一种):
Edit the migration files to put changes that have already been applied (whether Django did it or you did it manually does not matter) into lower number migrations, and put everything you need done into higher numbered files. Now you can
--fake
the lower number ones, and run the higher numbered ones as normal. Let's say you have 10 changes you made to your models, and 5 of those changes are actually in the database already, but Django doesn't know about them.. so when you runmakemigrations
, a new migration is created with all 10 changes. This will normally fail because the database server can't for example add a column that already exists. Move these already-applied changes out of your new migration file, into the previous (already applied) migration file. Django will then assume that these were applied with the previous migration and will not try to apply them again. You can thenmigrate
as normal and the new changes will be applied.If you don't want to touch your older migration file, a cleaner way to do this is to first run
makemigrations --empty appname
to create an empty migration file. Then runmakemigrations
which will create another migration with all the changes that Django thinks need to be done. Move the already done migrations from that file into the empty migration you created.. then--fake
that one. This will put Django's understanding of what the database looks like will be in sync with reality and you can thenmigrate
as normal, applying the changes in the last migration file.Get rid of any new migrations you just created using makemigrations. Now, comment out or put back anything in your models that has not been applied to the database, leaving your code matching what's actually in the database. Now you can do
makemigrations
andmigrate appname --fake
and you will get things back in sync. Then uncomment your new code and run 'makemigrations' thenmigrate
as normal and the changes will be applied. If the changes are small (for example, adding a few fields), sometimes this is easiest. If the changes are large, it isn't....You can go ahead and (carefully) make the database changes yourself, bringing the database up to date. Now just run
migrate --fake
and if you didn't mess up then everything will be ok. Again, this is easy for smaller changes, not as easy for complicated ones.You can run
manage.py sqlmigrate > mychanges.sql
. This generatesmychanges.sql
containing all the SQL Django WOULD have executed against the database. Now edit that file to remove any changes that have already been applied, leaving what needs to be done. Execute that SQL usingpgadmin
orpsql
(you're using postgresql I hope). Now the changes have all been made.. so you can runmanage.py migrate --fake
, this will bring Django into sync with reality and you should be all set. If your SQL skills are sufficient, this is probably the most straightforward solution.
编辑迁移文件,将已经应用的更改(无论是 Django 做的还是您手动做的都没有关系)放入较低编号的迁移中,并将您需要完成的所有内容放入较高编号的文件中。现在您可以
--fake
使用较低的数字,并像往常一样运行较高的数字。假设您对模型进行了 10 次更改,其中 5 次实际上已经在数据库中,但是 Django 不知道它们......所以当您运行时makemigrations
,将创建一个包含所有 10 个更改的新迁移。这通常会失败,因为数据库服务器不能例如添加已经存在的列。将这些已应用的更改从新迁移文件中移到先前(已应用)的迁移文件中。然后 Django 会假设这些是在之前的迁移中应用的,并且不会再次尝试应用它们。然后migrate
,您可以像往常一样应用新的更改。如果您不想修改旧的迁移文件,一个更简洁的方法是首先运行
makemigrations --empty appname
以创建一个空的迁移文件。然后运行makemigrations
这将创建另一个迁移,其中包含 Django 认为需要完成的所有更改。将已经完成的迁移从该文件移动到您创建的空迁移中......然后--fake
是那个。这将使 Django 对数据库外观的理解与现实同步,然后您可以migrate
像往常一样应用上一个迁移文件中的更改。摆脱您刚刚使用 makemigrations 创建的任何新迁移。现在,注释掉或放回模型中尚未应用于数据库的任何内容,让您的代码与数据库中的实际内容相匹配。现在你可以做
makemigrations
并且migrate appname --fake
你会让事情恢复同步。然后取消注释您的新代码并运行“makemigrations”,然后照常运行migrate
,更改将被应用。如果更改很小(例如,添加几个字段),有时这是最简单的。如果变化很大,那就不是......您可以继续(小心地)自己更改数据库,使数据库保持最新。现在就跑
migrate --fake
,如果你没有搞砸,那么一切都会好起来的。同样,这对于较小的更改很容易,对于复杂的更改则不那么容易。你可以运行
manage.py sqlmigrate > mychanges.sql
。这将生成mychanges.sql
包含对数据库执行的所有 SQL Django。现在编辑该文件以删除已应用的任何更改,留下需要完成的工作。使用pgadmin
or执行该 SQLpsql
(我希望您使用的是 postgresql)。现在所有更改都已完成..所以您可以运行manage.py migrate --fake
,这将使 Django 与现实同步,您应该已准备就绪。如果您的 SQL 技能足够,这可能是最直接的解决方案。
I should add two warnings:
我应该添加两个警告:
First, if you apply a later migration, eg 0003_foobar.py, and then things don't work out and you decide to try going back and apply 0002_bazbuz.py, then Django will TAKE STUFF OUT OF YOUR DATABASE. For example a column you might have added in 0003 will be dropped along with its data. Since you say you can't lose data, be very careful about going back.
首先,如果您应用以后的迁移,例如 0003_foobar.py,然后事情没有解决,并且您决定尝试返回并应用 0002_bazbuz.py,那么 Django 将从您的数据库中取出东西。例如,您可能在 0003 中添加的列将与其数据一起被删除。既然你说你不能丢失数据,那么回去要非常小心。
Second, do not rush into running --fake
migrations. Make sure that the entire migration you are about to fake is actually in the database already. Else it gets very confusing. If you do regret faking migrations and don't want to roll back, you can erase django's knowledge of the faked migration by deleting that row from the django_migrations
table. It is ok to do this.. if you understand what you are doing. If you know that the migration really was not applied, then it's ok.
其次,不要急于运行--fake
迁移。确保您要伪造的整个迁移实际上已经在数据库中。否则它会变得非常混乱。如果您确实后悔伪造迁移并且不想回滚,您可以通过从django_migrations
表中删除该行来消除 Django 对伪造迁移的了解。可以这样做.. 如果你明白你在做什么。如果您知道确实没有应用迁移,那么没关系。
回答by slogan621
This blog post really nails it. https://simpleisbetterthancomplex.com/tutorial/2016/07/26/how-to-reset-migrations.html
这篇博文真的很能说明问题。https://simpleisbetterthancomplex.com/tutorial/2016/07/26/how-to-reset-migrations.html
Let me summarize the steps in his scenario 2 (you have a production database and want to change schema/models in one or more apps). In my case, I had two apps, queue and routingslip, that had model modifications that I needed to apply to a production system. Key was I already had the database, so this is where --fake-initial comes into play.
让我总结一下他的场景 2 中的步骤(您有一个生产数据库并且想要更改一个或多个应用程序中的架构/模型)。就我而言,我有两个应用程序,queue 和 routingslip,它们具有我需要应用于生产系统的模型修改。关键是我已经有了数据库,所以这就是 --fake-initial 发挥作用的地方。
Here are the steps I followed. As always, backup everything before starting. I do work in a VM so I just took a snapshot before going forward.
这是我遵循的步骤。与往常一样,在开始之前备份所有内容。我确实在 VM 中工作,所以在继续之前我只是拍了一张快照。
1) Remove the migration history for each app.
1) 删除每个应用程序的迁移历史记录。
python manage.py migrate --fake queue zero
python manage.py migrate --fake routingslip zero
2) Blow away any migration files in the entire project within which the app(s) reside.
2) 清除应用程序所在的整个项目中的所有迁移文件。
find . -path "*/migrations/*.py" -not -name "__init__.py" -delete
find . -path "*/migrations/*.pyc" -delete
3) Make migrations
3)进行迁移
python manage.py makemigrations
4) Apply the migrations, faking initial because the database already exists and we just want the changes:
4)应用迁移,伪造初始,因为数据库已经存在,我们只想要更改:
python manage.py migrate --fake-initial
Worked great for me.
对我很有用。