带有转储数据和迁移的 Django 备份策略

Django backup strategy with dumpdata and migrations

this question, I set up a dumpdata-based backup system for my database. The setup is akin to running a cron script that calls dumpdata and moves the backup to a remote server, with the aim of simply using loaddata to recover the database. However, I'm not sure this plays well with migrations. loaddata now has an ignorenonexistent 开关处理已删除的 models/fields,但它无法解决使用一次性默认值添加列或应用 RunPython 代码的情况。

在我看来,有两个子问题需要解决:


我对如何在不引入大量开销的情况下解决第一个问题感到困惑。为每个包含 {app_name: migration_number} 映射的备份保存一个额外文件是否足够?

第一个问题解决后第二个问题我觉得比较容易,因为过程大致是:

  1. 创建新数据库
  2. 运行 迁移到每个应用程序的适当点
  3. 使用给定的夹具文件调用 loaddata
  4. 运行 其余迁移

this question 中有一些代码(链接自错误报告)我认为可以针对此目的进行调整。

由于这些是数据库的 regular/large 快照,我不想将它们保留为数据迁移,使迁移目录变得混乱。

我正在采取以下步骤在我的项目的任何实例之间备份、恢复或传输我的 postgresql 数据库:

我们的想法是保持尽可能少的迁移,就好像 manage.py makemigrations 是第一次在空数据库上 运行 一样。

假设我们的开发环境有一个可用的数据库。此数据库是生产数据库 的当前副本,不应对任何更改开放 。我们添加了模型、更改了属性等,这些操作产生了额外的迁移。

现在数据库已准备好迁移到生产环境,如前所述,生产环境未对 public 开放,因此不会以任何方式更改。为了实现这一点:

  • 我在开发环境中执行正常程序
  • 我把项目拷贝到生产环境中
  • 我在生产环境执行正常程序

我们对开发环境进行了更改。生产数据库中不应发生任何更改,因为 它们将被覆盖

正常程序

首先,我有一个项目目录的备份(其中包括一个 requirements.txt 文件),一个数据库的备份,当然还有 git 是我的一个朋友。

  1. 我做了一个 dumpdata 备份以备不时之需。不过,dumpdata有些严重 limitations regarding content types, permissions or other cases where a natural foreignkey应该用:

    ./manage.py dumpdata --exclude auth.permission --exclude contenttypes  --exclude admin.LogEntry --exclude sessions --indent 2 > db.json
    
  2. 我取一个pg_dump备份使用:

    pg_dump -U $user -Fc $database --exclude-table=django_migrations > path/to/backup-dir/db.dump
    
  3. 只有当我想将现有的迁移合并为一个时,我才会从每个应用程序中删除所有迁移。

    在我的例子中,migrations 文件夹是一个符号链接,所以我使用以下脚本:

    #!/bin/bash
    for dir in $(find -L -name "migrations")
    do
      rm -Rf $dir/*
    done
    
  4. 我删除并重新创建数据库:

    例如,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 ;'"
    
  5. 我从转储中恢复数据库:

    pg_restore -Fc -U $username -d $database path/to/backup-dir/db.dump
    
  6. 如果在步骤 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
    
  7. 我使用假迁移进行迁移:

    ./manage.py migrate --fake
    

如果出现完全错误并且一切都是 ***,(这确实可能发生),我可以使用备份将一切恢复到以前的工作状态。如果我想使用第一步中的 db.json 文件,它是这样的:

当pg_dump或pg_restore失败时

我执行以下步骤:

  • 3(删除迁移)
  • 4(删除并重新创建数据库)
  • 6(进行迁移)

然后:

  • 应用迁移:

    ./manage.py migrate
    
  • 从 db.json:

    加载数据
    ./manage.py loaddata path/to/db.json
    

然后我试着找出我之前的努力没有成功的原因。

成功执行这些步骤后,我将项目复制到服务器并对该框执行相同的操作。

这样,我总是保持最少的迁移次数,并且我能够对共享同一项目的任何框使用 pg_dumppg_restore