带有转储数据和迁移的 Django 备份策略
Posted
技术标签:
【中文标题】带有转储数据和迁移的 Django 备份策略【英文标题】:Django backup strategy with dumpdata and migrations 【发布时间】:2016-04-21 16:36:58 【问题描述】:与this question 一样,我为我的数据库设置了一个基于dumpdata
的备份系统。该设置类似于运行一个调用dumpdata
并将备份移动到远程服务器的cron 脚本,目的是简单地使用loaddata
来恢复数据库。但是,我是not sure this plays well with migrations。 loaddata
现在有一个 ignorenonexistent
开关来处理已删除的模型/字段,但它无法解决使用一次性默认值添加列或应用 RunPython
代码的情况。
在我看来,有两个子问题需要解决:
使用每个应用的当前版本标记每个dumpdata
输出文件
将固定装置拼接到迁移路径中
我对如何在不引入大量开销的情况下解决第一个问题感到困惑。每次备份保存一个包含app_name: migration_number
映射的额外文件就足够了吗?
第一个问题解决后,我认为第二个问题会更容易,因为过程大致是:
-
创建一个新数据库
将迁移向前运行到每个应用的适当点
使用给定的夹具文件调用
loaddata
运行其余的迁移
this question(链接自错误报告)中有一些我认为可以用于此目的的代码。
由于这些是数据库的相当常规/大的快照,我不想将它们作为数据迁移保留在迁移目录中。
【问题讨论】:
这不能回答您的问题,但我发现使用数据库转储是一种非常简单的备份解决方案。我对 Postgres 使用pg_dump
,对 mysql 数据库使用 mysqldump
。
如何更简单?如果您在备份后应用迁移,您最终不会遇到同样的问题吗?
您的数据库转储包含数据库的完整副本。这包括django_migrations
表。在数据库转储之前创建的迁移将不会再次运行,只会运行比数据库转储更新的迁移。
【参考方案1】:
我正在采取以下步骤在我的项目的任何实例之间备份、恢复或传输我的 postgresql 数据库:
我们的想法是尽可能减少迁移,就像manage.py makemigrations
第一次在空数据库上运行一样。
假设我们的开发环境有一个工作数据库。此数据库是生产数据库的当前副本不应对任何更改开放。我们添加了模型、更改了属性等,这些操作产生了额外的迁移。
现在数据库已准备好迁移到生产环境,如前所述,该数据库不向公众开放,因此不会以任何方式进行更改。为了实现这一点:
我在开发环境中执行正常程序。 我将项目复制到生产环境。 我在生产环境中执行正常程序我们在开发环境中进行更改。生产数据库中不应发生任何更改,因为它们将被覆盖。
正常程序
首先,我有一个项目目录的备份(其中包括一个 requirements.txt 文件),一个数据库的备份 - 当然 - git
是我的朋友。
我会备份dumpdata
以备不时之需。但是,dumpdata
在内容类型、权限或应使用natural foreignkey 的其他情况方面有一些严重 limitations:
./manage.py dumpdata --exclude auth.permission --exclude contenttypes --exclude admin.LogEntry --exclude sessions --indent 2 > db.json
我使用pg_dump
备份:
pg_dump -U $user -Fc $database --exclude-table=django_migrations > path/to/backup-dir/db.dump
仅当我想将现有迁移合并为一个时,我才会从每个应用程序中删除所有迁移。
在我的情况下,migrations
文件夹是一个符号链接,所以我使用以下脚本:
#!/bin/bash
for dir in $(find -L -name "migrations")
do
rm -Rf $dir/*
done
我删除并重新创建数据库:
例如,一个 bash 脚本可以包含以下命令:
su -l postgres -c "PGPASSWORD=$password psql -c 'drop database $database ;'"
su -l postgres -c "createdb --owner $username $database"
su -l postgres -c "PGPASSWORD=$password psql $database -U $username -c 'CREATE EXTENSION $extension ;'"
我从转储中恢复数据库:
pg_restore -Fc -U $username -d $database path/to/backup-dir/db.dump
如果迁移在第 3 步中被删除,我会通过以下方式重新创建它们:
./manage.py makemigrations <app1> <app2> ... <appn>
...通过使用以下脚本:
#!/bin/bash
apps=()
for app in $(find ./ -maxdepth 1 -type d ! -path "./<project-folder> ! -path "./.*" ! -path "./")
do
apps+=($app#??)
done
all_apps=$(printf "%s " "$apps[@]")
./manage.py makemigrations $all_apps
我使用虚假迁移进行迁移:
./manage.py migrate --fake
万一出现完全错误并且一切都是 ***,(确实会发生这种情况),我可以使用备份将所有内容恢复到之前的工作状态。如果我想使用第一步中的db.json
文件,如下所示:
当 pg_dump 或 pg_restore 失败时
我执行以下步骤:
3(删除迁移) 4(删除并重新创建数据库) 6 (makemigrations)然后:
应用迁移:
./manage.py migrate
从 db.json 加载数据:
./manage.py loaddata path/to/db.json
然后我试图找出为什么我之前的努力没有成功。
成功执行这些步骤后,我将项目复制到服务器并对该框执行相同的操作。
这样,我始终保持最少的迁移次数,并且我可以将pg_dump
和pg_restore
用于共享同一项目的任何盒子。
【讨论】:
首先,感谢您的完整解释!我想知道,如果有多个迁移,你会怎么做:a)安装 db b)migration1 c)dumpdb d)migration2。我认为你不能将你的转储数据库导入a),你必须先进行migration1。但是,如何防止系统进行第二次迁移? @JulienGreard 这是一种自相残杀但“舒缓”的方法,因为migrations 非常微妙,many times 无法自动复制。但是,想法是在应用所有迁移后,备份数据库,删除所有内容并重新安装。类似squahing 的东西在类固醇上......当项目已经部署时不是很有用,但是当你生气时很性感! 对不起,我不明白你的回答。如果我的备份不符合“原始”数据库(未应用迁移)并且不符合最新的数据库(应用了所有迁移),我不明白该怎么办。如果我的备份是在两次迁移之间进行的,我想我会被卡住。我必须先进行 N 次迁移,然后“加载数据”我的备份,然后再应用 M 次其他迁移。我不知道这种方法是如何告诉 Django 只应用前 N 个迁移。我不知道我是否让自己更清楚。告诉我这是否没有意义;-) @JulienGreardpg_restore
应该在反映数据库结构的代码上执行,就像执行pg_dump
时一样。 ./manage.py restore data
应该遇到一个空数据库,该数据库已经在反映数据库结构的代码上执行了./manage.py migrate
,就像执行./manage.py dumpdata
时一样。但是,最好打开一个新问题,详细了解您所面临的情况。
为什么不在步骤 1 中使用--natural-foreign
标志?以上是关于带有转储数据和迁移的 Django 备份策略的主要内容,如果未能解决你的问题,请参考以下文章