11

这个问题中,我为我的数据库设置了一个dumpdata基于备份的系统。该设置类似于运行一个 cron 脚本,该脚本调用dumpdata并将备份移动到远程服务器,目的是简单地loaddata用于恢复数据库。但是,我不确定这是否适用于 migrationsloaddata现在有一个ignorenonexistent开关来处理已删除的模型/字段,但它无法解决使用一次性默认值添加列或应用RunPython代码的情况。

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

  • dumpdata使用每个应用程序的当前版本标记每个输出文件
  • 将夹具拼接到迁移路径中

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

我认为第二个问题一旦解决了第一个问题就容易了,因为过程大致是:

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

这个问题中有一些代码(链接自错误报告),我认为可以为此目的进行调整。

由于这些是数据库的相当常规/大型快照,我不想将它们作为数据迁移保留在迁移目录中。

4

1 回答 1

21

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

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

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

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

  • 我在开发环境中执行正常程序。
  • 我将项目复制到生产环境。
  • 我在生产环境中执行正常程序

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

正常程序

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

  1. 我会dumpdata备份以备不时之需。但是,对于内容类型、权限或应使用自然外键的其他情况,dumpdata有一些严重的 限制:

    ./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共享同一项目的任何盒子。

于 2017-01-19T01:28:41.453 回答