[英]Django: loaddata in migrations errors
Something really annoying is happening to me since using Django migrations (not south) and using loaddata for fixtures inside of them. 自从使用Django迁移(不是南方)并在其中使用loaddata for fixture以来,我发生了一些令人讨厌的事情。
Here is a simple way to reproduce my problem: 这是一个重现我的问题的简单方法:
Testmodel
with 1 field field1
(CharField or whatever) 使用1字段field1
(CharField或其他)创建一个新模型Testmodel
0001
) with makemigrations
使用makemigrations
创建关联的迁移(假设为0001
) testmodel.json
将数据转储到fixture testmodel.json
call_command('loaddata', 'testmodel.json')
: migration 0002
使用call_command('loaddata', 'testmodel.json')
创建迁移:迁移0002
field2
在模型中添加一些新字段: field2
0003
) 创建关联的迁移( 0003
) Now, commit that, and put your db in the state just before the changes: ./manage.py migrate myapp zero
. 现在,提交,并将您的数据库置于更改之前的状态: ./manage.py migrate myapp zero
。 So you are in the same state as your teammate that didn't get your changes yet. 所以你和你的队友处于同样的状态,但还没有得到你的改变。
If you try to run ./manage.py migrate
again you will get a ProgrammingError
at migration 0002 saying that "column field2 does not exist". 如果您再次尝试运行./manage.py migrate
,则会在迁移0002时收到ProgrammingError
,说“column field2不存在”。
It seems it's because loaddata is looking into your model (which is already having field2
), and not just applying the fixture to the db. 这似乎是因为loaddata正在查看你的模型(已经有了field2
),而不仅仅是将数据包应用到db。
This can happen in multiple cases when working in a team, and also making the test runner fail. 在团队中工作时,可能会发生多种情况,也会导致测试运行器失败。
Did I get something wrong? 我弄错了吗? Is it a bug? 这是一个错误吗? What should be done is those cases? 应该做些什么呢?
-- -
I am using django 1.7 我正在使用django 1.7
loaddata
command will simply call serializers. loaddata
命令只会调用序列化程序。 Serializers will work on models state from your models.py
file, not from current migration, but there is little trick to fool default serializer. 序列化程序将处理models.py
文件中的模型状态,而不是当前迁移,但是没有什么可以欺骗默认的序列化程序。
First, you don't want to use that serializer by call_command
but rather directly: 首先,您不希望通过call_command
使用该序列化call_command
,而是直接使用该序列化call_command
:
from django.core import serializers
def load_fixture(apps, schema_editor):
fixture_file = '/full/path/to/testmodel.json'
fixture = open(fixture_file)
objects = serializers.deserialize('json', fixture, ignorenonexistent=True)
for obj in objects:
obj.save()
fixture.close()
Second, monkey-patch apps registry used by serializers: 其次,序列化器使用的monkey-patch apps注册表:
from django.core import serializers
def load_fixture(apps, schema_editor):
original_apps = serializers.python.apps
serializers.python.apps = apps
fixture_file = '/full/path/to/testmodel.json'
fixture = open(fixture_file)
objects = serializers.deserialize('json', fixture, ignorenonexistent=True)
for obj in objects:
obj.save()
fixture.close()
serializers.python.apps = original_apps
Now serializer will use models state from apps
instead of default one and whole migration process will succeed. 现在,序列化程序将使用apps
模型状态而不是默认状态,整个迁移过程将成功。
When you run python manage.py migrate
it's trying to load your testmodel.json
in fixtures
folder, but your model (after updated) does not match with data in testmodel.json
. 当你运行python manage.py migrate
它试图在fixtures
文件夹中加载你的testmodel.json
,但你的模型(更新后)与testmodel.json
数据不匹配。 You could try this: 你可以试试这个:
Change your directory from fixture
to _fixture
. 将目录从fixture
更改为_fixture
。
Run python manage.py migrate
运行python manage.py migrate
Optional, you now can change _fixture
by fixture
and load your data as before with migrate
command or load data with python manage.py loaddata app/_fixtures/testmodel.json
可选,您现在可以通过fixture
更改_fixture
并像以前一样使用python manage.py loaddata app/_fixtures/testmodel.json
加载数据来使用migrate
命令或加载数据
To expand on the answer from GwynBleidD and mix in this issue since Postgres won't reset the primary key sequences when loaded this way ( https://stackoverflow.com/a/14589706/401636 ) 扩展GwynBleidD的答案并混合使用此问题,因为Postgres在以这种方式加载时不会重置主键序列( https://stackoverflow.com/a/14589706/401636 )
I think I now have a failsafe migration for loading fixture data. 我想我现在有一个用于加载夹具数据的故障安全迁移。
utils.py: utils.py:
import os
from io import StringIO
import django.apps
from django.conf import settings
from django.core import serializers
from django.core.management import call_command
from django.db import connection
os.environ['DJANGO_COLORS'] = 'nocolor'
def reset_sqlsequence(apps=None, schema_editor=None):
"""Suitable for use in migrations.RunPython"""
commands = StringIO()
cursor = connection.cursor()
patched = False
if apps:
# Monkey patch django.apps
original_apps = django.apps.apps
django.apps.apps = apps
patched = True
else:
# If not in a migration, use the normal apps registry
apps = django.apps.apps
for app in apps.get_app_configs():
# Generate the sequence reset queries
label = app.label
if patched and app.models_module is None:
# Defeat strange test in the mangement command
app.models_module = True
call_command('sqlsequencereset', label, stdout=commands)
if patched and app.models_module is True:
app.models_module = None
if patched:
# Cleanup monkey patch
django.apps.apps = original_apps
sql = commands.getvalue()
print(sql)
if sql:
# avoid DB error if sql is empty
cursor.execute(commands.getvalue())
class LoadFixtureData(object):
def __init__(self, *files):
self.files = files
def __call__(self, apps=None, schema_editor=None):
if apps:
# If in a migration Monkey patch the app registry
original_apps = serializers.python.apps
serializers.python.apps = apps
for fixture_file in self.files:
with open(fixture_file) as fixture:
objects = serializers.deserialize('json', fixture)
for obj in objects:
obj.save()
if apps:
# Cleanup monkey patch
serializers.python.apps = original_apps
And now my data migrations look like: 现在我的数据迁移看起来像:
# -*- coding: utf-8 -*-
# Generated by Django 1.11.1 on foo
from __future__ import unicode_literals
import os
from django.conf import settings
from django.db import migrations
from .utils import LoadFixtureData, reset_sqlsequence
class Migration(migrations.Migration):
dependencies = [
('app_name', '0002_auto_foo'),
]
operations = [
migrations.RunPython(
code=LoadFixtureData(*[
os.path.join(settings.BASE_DIR, 'app_name', 'fixtures', fixture) + ".json"
for fixture in ('fixture_one', 'fixture_two',)
]),
# Reverse will NOT remove the fixture data
reverse_code=migrations.RunPython.noop,
),
migrations.RunPython(
code=reset_sqlsequence,
reverse_code=migrations.RunPython.noop,
),
]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.