Django:迁移错误中的加载数据
Django: loaddata in migrations errors
自从使用 Django 迁移(不是南方)并为其中的固定装置使用加载数据后,我遇到了一些非常烦人的事情。
这是重现我的问题的简单方法:
- 使用 1 个字段
field1
(CharField 或其他)创建一个新模型 Testmodel
- 使用
makemigrations
创建关联的迁移(比方说 0001
)
- 运行迁移
- 并在新的 table
中添加一些数据
- 将数据转储到夹具中
testmodel.json
- 使用
call_command('loaddata', 'testmodel.json')
创建迁移:迁移 0002
- 向模型添加一些新字段:
field2
- 创建关联迁移(
0003
)
现在,提交它,并将您的数据库置于更改之前的状态:./manage.py migrate myapp zero
。所以你和你的队友处于相同的状态,但还没有得到你的更改。
如果您再次尝试 运行 ./manage.py migrate
您将在迁移 0002 处得到一个 ProgrammingError
说 "column field2 does not exist".
这似乎是因为 loaddata 正在查看您的模型(已经有 field2
),而不仅仅是将夹具应用于数据库。
在团队中工作时,这可能会在多种情况下发生,并且还会导致测试 运行 失败。
我是不是搞错了什么?这是一个错误吗?那些情况应该怎么办?
--
我正在使用 django 1.7
当你 运行 python manage.py migrate
它试图加载你的 testmodel.json
在 fixtures
文件夹中,但是你的模型(更新后)与 [=] 中的数据不匹配11=]。你可以试试这个:
将您的目录从 fixture
更改为 _fixture
。
运行 python manage.py migrate
可选,您现在可以将 _fixture
更改为 fixture
并像以前一样使用 migrate
命令加载数据或使用 python manage.py loaddata app/_fixtures/testmodel.json
loaddata
命令将简单地调用序列化程序。序列化程序将处理来自您的 models.py
文件的模型状态,而不是来自当前迁移的模型状态,但是有一些小技巧可以愚弄默认序列化程序。
首先,您不想通过 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()
其次,序列化程序使用的 monkey-patch 应用注册表:
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
现在序列化程序将使用来自 apps
的模型状态而不是默认状态,并且整个迁移过程将会成功。
扩展 GwynBleidD 的答案并解决这个问题,因为 Postgres 在以这种方式加载时不会重置主键序列 ()
我想我现在有一个用于加载夹具数据的故障安全迁移。
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
现在我的数据迁移看起来像:
# -*- 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,
),
]
自从使用 Django 迁移(不是南方)并为其中的固定装置使用加载数据后,我遇到了一些非常烦人的事情。
这是重现我的问题的简单方法:
- 使用 1 个字段
field1
(CharField 或其他)创建一个新模型Testmodel
- 使用
makemigrations
创建关联的迁移(比方说 - 运行迁移
- 并在新的 table 中添加一些数据
- 将数据转储到夹具中
testmodel.json
- 使用
call_command('loaddata', 'testmodel.json')
创建迁移:迁移0002
- 向模型添加一些新字段:
field2
- 创建关联迁移(
0003
)
0001
)
现在,提交它,并将您的数据库置于更改之前的状态:./manage.py migrate myapp zero
。所以你和你的队友处于相同的状态,但还没有得到你的更改。
如果您再次尝试 运行 ./manage.py migrate
您将在迁移 0002 处得到一个 ProgrammingError
说 "column field2 does not exist".
这似乎是因为 loaddata 正在查看您的模型(已经有 field2
),而不仅仅是将夹具应用于数据库。
在团队中工作时,这可能会在多种情况下发生,并且还会导致测试 运行 失败。
我是不是搞错了什么?这是一个错误吗?那些情况应该怎么办?
--
我正在使用 django 1.7
当你 运行 python manage.py migrate
它试图加载你的 testmodel.json
在 fixtures
文件夹中,但是你的模型(更新后)与 [=] 中的数据不匹配11=]。你可以试试这个:
将您的目录从
fixture
更改为_fixture
。运行
python manage.py migrate
可选,您现在可以将
_fixture
更改为fixture
并像以前一样使用migrate
命令加载数据或使用python manage.py loaddata app/_fixtures/testmodel.json
loaddata
命令将简单地调用序列化程序。序列化程序将处理来自您的 models.py
文件的模型状态,而不是来自当前迁移的模型状态,但是有一些小技巧可以愚弄默认序列化程序。
首先,您不想通过 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()
其次,序列化程序使用的 monkey-patch 应用注册表:
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
现在序列化程序将使用来自 apps
的模型状态而不是默认状态,并且整个迁移过程将会成功。
扩展 GwynBleidD 的答案并解决这个问题,因为 Postgres 在以这种方式加载时不会重置主键序列 ()
我想我现在有一个用于加载夹具数据的故障安全迁移。
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
现在我的数据迁移看起来像:
# -*- 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,
),
]