不使用固定装置的 Django 1.10 种子数据库

Posted

技术标签:

【中文标题】不使用固定装置的 Django 1.10 种子数据库【英文标题】:Django 1.10 seed database without using fixtures 【发布时间】:2018-04-28 00:54:24 【问题描述】:

所以我查看了documentation、SO question 和django-seed package,但这些似乎都不符合我的目标。

基本上,我想通过外部 API 以编程方式播种我的 Games 模型,但我能找到的所有信息似乎都依赖于首先生成一个夹具,这似乎是一个不必要的步骤。

例如,在 Ruby/Rails 中,您可以直接写入 seed.rb 并以任何所需的方式为数据库播种。

如果 Django 中提供了类似的功能,还是需要先从 API 生成夹具,然后再导入?

【问题讨论】:

Django 的manage.py loaddata 只是解析您的文本文件并将其插入到您的数据库中。您可以通过初始迁移以编程方式执行此操作,就像它在 linked documentation 底部推荐的那样。 是的,但我试图避免制作文本文件。似乎没有必要。 迁移不需要包含任何硬编码的初始数据,它们只是 Python 脚本。您可以只使用RunPython 迁移并创建一个函数,该函数从您的 API 流式传输数据并将其插入到您的数据库中。它会在初始迁移运行时自动运行,并且由于迁移会自动使用事务,因此可以防止损坏您的数据库状态。 现在和你在一起。我没有意识到 Django 的文档建议创建数据迁移。仍然习惯于不断变化的术语。感谢您花时间解释。 【参考方案1】:

您可以为此使用数据迁移。首先为您的应用创建一个空迁移:

$ python manage.py makemigrations yourappname --empty

在您的空迁移中,创建一个函数来加载您的数据并添加一个migrations.RunPython 操作。这是Django documentation on migrations的修改版本:

from __future__ import unicode_literals
from django.db import migrations

def stream_from_api():
    ...

def load_data(apps, schema_editor):
    # We can't import the Person model directly as it may be a newer
    # version than this migration expects. We use the historical version.
    Person = apps.get_model('yourappname', 'Person')

    for item in stream_from_api():
        person = Person(first=item['first'], last=item['last'], age=item['age'])
        person.save()

class Migration(migrations.Migration):
    dependencies = [('yourappname', '0009_something')]
    operations = [migrations.RunPython(load_data)]

如果您有很多简单的数据,您可能会从批量创建方法中受益:

from __future__ import unicode_literals
from django.db import migrations

def stream_from_api():
    ...

def load_data(apps, schema_editor):
    # We can't import the Person model directly as it may be a newer
    # version than this migration expects. We use the historical version.
    Person = apps.get_model('yourappname', 'Person')

    def stream_people():
        for item in stream_from_api():
            yield Person(first=item['first'], last=item['last'], age=item['age'])

    # Adjust (or remove) the batch size depending on your needs.
    # You won't be able to use this method if your objects depend on one-another
    Person.objects.bulk_create(stream_people(), batch_size=10000)

class Migration(migrations.Migration):
    dependencies = [('yourappname', '0009_something')]
    operations = [migrations.RunPython(load_data)]

迁移具有自动包含在事务中的额外好处,因此您可以随时停止迁移,并且不会使您的数据库处于不一致状态。

【讨论】:

在您的load_data 函数中,无论如何要使用apps.get_model() 加载基本用户模型?我知道有,只是不知道怎么做。 @Tunn: apps.get_model('auth', 'User') 应该可以工作。【参考方案2】:

在创建数据的Games 模型上编写一些类方法对您有用吗?推测此方法查询外部 API,将 Games() 对象打包为名为 games 的列表,然后使用 Games.objects.bulk_create(games) 将其插入数据库。

【讨论】:

这似乎相当理想。谢谢。

以上是关于不使用固定装置的 Django 1.10 种子数据库的主要内容,如果未能解决你的问题,请参考以下文章

迁移 Django 固定装置?

Django 固定装置:通过 JSON 将 HTML 数据导入 TextField

Django:在不指定主键的情况下创建固定装置?

在 django 单元测试中加载固定装置

清空桌子并装满固定装置

在 Django 中,您可以在不总是生成迁移的情况下运行种子数据吗?