Django:从sqlite迁移到PostgreSQL的最佳实践是什么?

32

我需要将一个复杂的项目从sqlite迁移到PostgreSQL。很多人似乎会遇到外键、数据截断等问题...

  • 是否有完全自动化的工具可用?
  • 在迁移之前,我需要检查一些数据或模式吗?

编辑:我尝试过django-command-extensionsDumpScript,但它在我的2GB RAM PC上无法运行我的当前数据集。

7个回答

38

根据我的经验,使用SQL导入和导出不起作用。

相反,您应该按照以下顺序进行:

1. 将数据库内容转储为JSON格式

$ ./manage.py dumpdata > dump.json

2. 在settings.py中切换后端

DATABASES = {
    # COMMENT OUT:
    # 'default': dj_database_url.config(default='sqlite:////full/path/to/your/database/file.sqlite'),
    # ADD THIS INSTEAD:
    'default': dj_database_url.config(default='postgres://localhost:5432/postgres_db_name'),
}

3. 同步并迁移新数据库到相同的表结构

$ ./manage.py syncdb
$ ./manage.py migrate

4. 将JSON加载到新数据库中。

$ ./manage.py loaddata dump.json

5. 恭喜!现在新数据已经存储到您的Postgres数据库中。


1
小心,这只适用于较小的数据库大小。loaddata似乎会将整个JSON加载到RAM中。 - pufferfish
10
这会导致一个错误:django.db.utils.IntegrityError: 安装fixture 'dump.json'时出现问题:无法加载contenttypes.ContentType(pk=3):重复的关键值违反唯一约束条件 "django_content_type_app_label_76bd3d3b_uniq" 详情:(app_label, model)=(auth, group)的键已经存在。 - matandked
3
针对我在上一个评论中的错误,似乎在使用loaddata之前您需要应用'TRUNCATE django_content_type CASCADE;'。@Nimo,您能更新您的回答吗? - matandked
@matandked 我也遇到了同样的问题。我可以将你的修复编辑到答案中,但是当答案本身没有任何问题时,我不确定编辑的礼仪是什么。 - Ellis
3
dumpdata --natural-primary --natural-foreign 可以避免这个问题,我会把它加入到答案中。 - damio

23
以下是对 Django 1.7+ 的 Nimo's 答案Stephen's 答案 的细化:
  1. ./manage.py dumpdata --natural-primary --natural-foreign > dump.json
  2. settings.py 中的 DATABASES 更改为指向新的(PostgreSQL)数据库。
  3. ./manage.py migrate
  4. ./manage.py loaddata dump.json
我遇到的一个问题是,SQLite 似乎没有实际强制执行 CharField 的最大长度限制。在我的情况下,这使得 loaddata 步骤失败。我能够通过以下方式找到(并删除)具有过长 CharField 值的模型实例:
MyModel.objects.extra(where=["LENGTH(text) > 20"]).delete()

在执行上述步骤1之前,我曾经做过这件事,然后一切正常。


这里的一个问题是迁移写入数据(例如默认内容类型)。我进行迁移,然后刷新,再将索引截断至零,然后加载数据。这需要记住很多东西,而且我总是会忘记某些步骤,但它确实有效。 - Oli
这个答案比大多数点赞最多的答案更好,因为在某些情况下使用 dumpdata > dump.json 是行不通的。 - sgauri

9

我从未尝试过这样做,但我会尝试以下步骤:

  1. 停止运行服务器
  2. 运行命令:python manage.py dumpdata
  3. 修改settings.py文件,将其指向新创建的postgres数据库
  4. 运行命令:python manage.py loaddata

1
python manage.py dumpdata -> 我的电脑内存不足... 如果我逐个应用程序尝试,情况会稍微好一些,但是导入仍然无法正常工作。 - Pierre-Jean Coudert

6
根据@Nimo的回答,在Django 1.9及以后版本(适用于Django 1.7)中,使用从"syncdb"开始的指令行不起作用。相反,请使用以下命令:python manage.py migrate
这里是Postgres设置配置:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myproject',
        'USER': 'myprojectuser',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '',
    }
}

4
另一种方法是使用多个数据库。
请阅读此部分:http://docs.djangoproject.com/en/dev/topics/db/multi-db/ 重要的是您要认真阅读这一部分。
从我的理解来看,这意味着如果新数据库中没有数据(例如来自 fixtures),那么您可以执行以下操作: http://docs.djangoproject.com/en/dev/topics/db/multi-db/#moving-an-object-from-one-database-to-another
queryset = MyModel.objects.using("old-db").all()
for obj in queryset:
    obj.save(using="new-db")

因为这样可以保留主键,所以我认为不会出现任何外键问题。


3
我成功的做法是使用Ruby运行Sequel。 只需要执行以下命令:
gem install sequel

您需要在系统上安装postgres、sqlite和ruby的开发包。运行以下命令:
gem install pg sqlite3

在PostgreSQL上创建一个空数据库,比如说testDB,并给一个用户分配授权许可。

从命令提示符中运行以下命令:

sequel -C sqlite:///path/to/sqlitedb.db postgres://user:password@host/testDB

这段代码将不会出现错误。

更改您的Django项目设置以适用于Postgres数据库。

运行:

./manage migrate (非必须)

启动服务器。


让我开心了一整天,谢谢 :) - ishak O.

2
首先,我建议您尝试一个简单的步骤:

sqlite3 sqllitedb .dump | psql postgresdb

在这个时候,只需要测试一下。在Django中编写一些测试脚本来输出每个应用程序的样本记录集,然后进行比较以确保它们完全相同。如果是,则您的转换可能没有问题。
如果这样做不起作用...
我建议不要使用Django来转储和加载数据,因为我猜它没有针对此进行优化。
相反,我会创建一个具有正确的PostgreSQL数据库设置的应用程序的第二个版本,运行syncdb以创建所有表,然后使用其他工具将数据从mysqllite复制到PostgreSQL。
事实上,大多数在转换数据时出现的问题都在于表定义等方面。这些似乎是最特殊的。如果您可以生成仅为表内容转储的SQL脚本,则应该是相当标准的SQL INSERT INTO命令。
老实说,我看不出为什么会出现外键问题。假设sqlite正在创建准确的外键(为什么不呢?),那么就没有办法无法正确复制。实际上,外键并不是特殊的数据形式。UserProfile.user_id字段包含不正确的值的可能性不比UserProfile.photo字段更大。如果外键的担忧是这些字段本身未被正确标识为外键字段(即没有约束),则使用syncdb首先创建数据库的选项将解决该问题。
至于截断:据我所知,如果数据即将被截断,PostgreSQL会抛出严重错误。我不知道sqlite是否也是这种情况,或者它只是默默地截断。无论哪种方式,假设sqlite在导出时没有以某种方式混淆数据,则字段应包含适合其所在字段的正确长度的数据。我能想到可能会影响此事的唯一事情是字符编码,因此请确保PostgreSQL字段具有与sqlite表相同的编码,至少在导入期间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接