我最近从Django 1.6切换到了1.7,并且开始使用迁移(我从来没有使用过South)。
在1.7之前,我使用fixture/initial_data.json
文件加载初始数据,该文件使用python manage.py syncdb
命令加载(创建数据库时)。
现在,我开始使用迁移,这种行为已经过时了:
如果应用使用迁移,则不会自动加载fixture。由于Django 2.0中的应用需要迁移,因此此行为被视为弃用。如果您想加载应用的初始数据,请考虑在数据迁移中进行。(https://docs.djangoproject.com/en/1.7/howto/initial-data/#automatically-loading-initial-data-fixture)
官方文件没有明确的例子说明如何做,所以我的问题是:
使用数据迁移导入此类初始数据的最佳方法是什么:
1.编写Python代码,多次调用mymodel.create(...)
,
1.使用或编写Django函数(like calling loaddata
)从JSON fixture文件加载数据。
我更喜欢第二种选择。
我不想使用南方,因为Django现在似乎可以自己做了。
8条答案
按热度按时间puruo6ea1#
假设
<yourapp>/fixtures/initial_data.json
中有一个fixture文件1.创建空迁移:
在Django 1.7中:
在Django 1.8+中,你可以提供一个名字:
1.编辑迁移文件
<yourapp>/migrations/0002_auto_xxx.py
2.1.自定义实现,灵感来自Django的
loaddata
(初始答案):2.2.
load_fixture
的一个更简单的解决方案(根据@juliocesar的建议):2.3.* * 最简单:**使用
app_label
调用loaddata
将从<yourapp>
的fixtures
目录自动加载fixture:app_label
,loaddata将尝试从allapps fixture目录加载fixture
文件名(您可能不需要)。*1.运行它
pcrecxhr2#
简短版本
您不应*****在数据迁移中直接使用
loaddata
管理命令。长版本
loaddata
利用使用最新模型的django.core.serializers.python.Deserializer
来反序列化迁移中的历史数据。这是不正确的行为。例如,假设有一个使用
loaddata
管理命令从fixture加载数据的数据迁移,并且它已经应用到您的开发环境中。稍后,您决定向相应的模型添加一个新的 * required * 字段,因此您这样做了,并针对更新后的模型进行了新的迁移(并且可能在
./manage.py makemigrations
提示您时向新字段提供一次性值)。您运行下一次迁移,一切正常。
最后,您已经完成了Django应用程序的开发,并将其部署到生产服务器上,现在是时候在生产环境中从头开始运行整个迁移了。
但是,数据迁移失败,原因是
loaddata
命令反序列化后的模型,代表当前代码,不能保存,新增加的 * required * 字段数据为空,原来的fixture缺少必要的数据!但是,即使您使用新字段所需的数据更新夹具,数据迁移仍然失败。当数据迁移运行时,将相应列添加到数据库的 * 下一个 * 迁移尚未应用。您不能将数据保存到不存在的列!
*结论:*在数据迁移中,
loaddata
命令会在模型和数据库之间引入潜在的不一致。您绝对不应该在数据迁移中直接使用它。解决方案
loaddata
命令依赖于django.core.serializers.python._get_model
函数从fixture获取相应的模型,fixture将返回模型的最新版本。我们需要对其进行monkey-patch,以便它获取历史模型。i86rm4rw3#
受一些评论(即n__o)的启发,以及我有很多
initial_data.*
文件分布在多个应用程序上的事实,我决定创建一个Django应用程序,以方便创建这些数据迁移。使用django-migration-fixture,您只需运行以下管理命令,它就会在您的所有
INSTALLED_APPS
中搜索initial_data.*
文件,并将其转换为数据迁移。有关安装/使用说明,请参见django-migration-fixture。
8ehkhllq4#
为了给予你的数据库一些初始数据,编写一个数据迁移,在数据迁移中,使用RunPython函数加载你的数据。
不要编写任何loaddata命令,因为这种方式已被弃用。
您的数据迁移将只运行一次。迁移是一个有序的迁移序列。当003_xxxx.py迁移运行时,django迁移会在数据库中写入这个应用被迁移到这个(003),并且将只运行下面的迁移。
3htmauhk5#
不幸的是,上面的解决方案并不适用于我,我发现每次我改变我的模型时,我都必须更新我的fixture,理想情况下,我应该编写数据迁移来类似地修改创建的数据和fixture加载的数据。
为了方便这个I wrote a quick function,它将在当前应用的
fixtures
目录中查找并加载一个fixture。将这个函数放入一个迁移中,在模型历史记录的点上匹配迁移中的字段。jv4diomz6#
在Django2.1上,我想加载一些带有初始数据的模型(比如国家名称)。
但我希望在执行初始迁移后立即自动执行此操作。
因此,我认为在每个需要加载初始数据的应用程序中都有一个
sql/
文件夹会很棒。然后,在该
sql/
文件夹中,我将拥有.sql
文件,其中包含将初始数据加载到相应模型所需的DML,例如:为了更好地描述,下面是包含
sql/
文件夹的应用的外观:另外,我发现在某些情况下,我需要
sql
脚本以特定的顺序执行,所以我决定在文件名前面加上一个连续的数字,如上图所示。然后,我需要一种方法来加载任何应用程序文件夹中的任何可用
SQLs
,方法是执行python manage.py migrate
。因此,我创建了另一个名为
initial_data_migrations
的应用程序,然后将此应用程序添加到settings.py
文件中的INSTALLED_APPS
列表中。然后,我在其中创建了一个migrations
文件夹,并添加了一个名为run_sql_scripts.py
的文件(实际上是一个自定义迁移)。如下图所示:我创建了
run_sql_scripts.py
,这样它就可以运行每个应用程序中所有可用的sql
脚本。当有人运行python manage.py migrate
时,这个脚本就会被触发。这个自定义的migration
还添加了相关的应用程序作为依赖项。这样,只有在所需的应用程序执行了0001_initial.py
迁移之后,它才会尝试运行sql
语句(我们不想尝试对不存在的表运行SQL语句)。下面是该脚本的源代码:
我希望有人觉得这对我有帮助,它对我很有效!如果你有任何问题,请让我知道。
注意:这可能不是最好的解决方案,因为我才刚刚开始使用Django,但是我仍然想与大家分享这个“操作方法”,因为我在谷歌上搜索的时候没有找到太多的信息。
2sbarzqh7#
在我看来,fixture有点糟糕。如果你的数据库经常变化,保持它们最新将很快成为一场噩梦。实际上,这不仅仅是我的观点,在书“Two Scoops of Django”中解释得更好。
相反,我将编写一个Python文件来提供初始设置。如果你需要更多的东西,我建议你看看Factory boy。
如果需要迁移某些数据,则应使用数据迁移。
还有"Burn Your Fixtures, Use Model Factories"关于使用固定装置。
nnvyjq4y8#
自然键呢?
尽管@rockallite的回答非常好,但它没有解释如何处理依赖于 * 自然键 * 而不是整数
pk
值的fixture。简化版
首先,请注意,通过使用
unittest.mock.patch
作为上下文管理器,并修补apps
而不是_get_model
,可以简化@rockallite的解决方案:只要您的装置 * 不 * 依赖于自然关键点,这就可以很好地工作。
如果他们 * 这样做 *,你很可能会看到一个
DeserializationError: ... value must be an integer...
。自然键的问题
实际上,
loaddata
使用django.core.serializers.deserialize()
来加载fixture对象。基于 * natural keys * 的fixture的反序列化依赖于两件事:
1.在模型的默认管理器上存在get_by_natural_key()方法
1.模型本身上natural_key()方法的存在
get_by_natural_key()
方法对于反序列化程序了解如何解释自然键而不是整数pk
值是必需的。这两种方法对于反序列化器来说都是必要的,以便通过自然键从数据库中
get
现有对象,如here所述。但是,迁移中可用的
apps
注册表使用历史模型,这些模型不能访问定制管理器或定制方法(如natural_key()
)。可能的解决方案:第一步
我们的定制模型管理器中缺少
get_by_natural_key()
方法的问题相对容易解决:只需在自定义管理器上设置use_in_migrations=True
,如文档中所述。这确保了您的历史模型可以在迁移期间访问当前的
get_by_natural_key()
,并且fixture加载现在应该成功了。但是,您的历史模型仍然没有
natural_key()
方法。因此,您的装置将被视为新对象,即使它们已经存在于数据库中。如果重新应用数据迁移,这可能会导致各种错误,例如:因此,实际上,您仍然错过了反序列化过程中类似get_or_create的行为。
要体验这一点,只需应用上述数据迁移(在测试环境中),然后回滚相同的数据迁移(不删除数据),然后重新应用数据迁移。
可能的解决方案:第二步
模型本身缺少
natural_key()
方法的问题比较难解决,一种解决方案是将当前模型中的natural_key()
方法分配给历史模型,例如:注:
natural_key
方法,这在某些场景中仍然会导致问题,但Django的use_in_migrations
选项也是如此。