最近Django 1.6から1.7に切り替えて、移行の使用を開始しました(Southを使用したことはありません)。
1.7より前は、fixture/initial_data.json
コマンドを使用してロードされたpython manage.py syncdb
ファイルで初期データをロードしていました(データベースの作成時)。
今、移行を使用し始めましたが、この動作は非推奨です:
アプリケーションが移行を使用する場合、フィクスチャの自動ロードはありません。 Django 2.0のアプリケーションでは移行が必要になるため、この動作は非推奨と見なされます。アプリの初期データをロードする場合は、データ移行で行うことを検討してください。 ( https://docs.djangoproject.com/en/1.7/howto/initial-data/#automatically-loading-initial-data-fixtures )
公式ドキュメント には、それを行う方法に関する明確な例がないため、私の質問は次のとおりです。
データ移行を使用してこのような初期データをインポートする最良の方法は何ですか:
mymodel.create(...)
を複数回呼び出してPythonコードを記述し、loaddata
の呼び出しのような )を使用または作成します。私は2番目のオプションを好みます。
Djangoがネイティブでできるようになったので、Southは使いたくありません。
Update:このソリューションが引き起こす可能性のある問題については、以下の@GwynBleidDのコメントを参照してください。また、将来のモデル変更に対してより耐久性のあるアプローチについては、以下の@Rockalliteの回答を参照してください。
<yourapp>/fixtures/initial_data.json
にフィクスチャファイルがあると仮定します
空の移行を作成します。
Django 1.7の場合:
python manage.py makemigrations --empty <yourapp>
Django 1.8+では、名前を指定できます。
python manage.py makemigrations --empty <yourapp> --name load_intial_data
移行ファイルを編集<yourapp>/migrations/0002_auto_xxx.py
2.1。 Django 'loaddata
(初期回答)に触発されたカスタム実装:
import os
from sys import path
from Django.core import serializers
fixture_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../fixtures'))
fixture_filename = 'initial_data.json'
def load_fixture(apps, schema_editor):
fixture_file = os.path.join(fixture_dir, fixture_filename)
fixture = open(fixture_file, 'rb')
objects = serializers.deserialize('json', fixture, ignorenonexistent=True)
for obj in objects:
obj.save()
fixture.close()
def unload_fixture(apps, schema_editor):
"Brutally deleting all entries for this model..."
MyModel = apps.get_model("yourapp", "ModelName")
MyModel.objects.all().delete()
class Migration(migrations.Migration):
dependencies = [
('yourapp', '0001_initial'),
]
operations = [
migrations.RunPython(load_fixture, reverse_code=unload_fixture),
]
2.2。 load_fixture
のより簡単なソリューション(@juliocesarの提案による):
from Django.core.management import call_command
fixture_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../fixtures'))
fixture_filename = 'initial_data.json'
def load_fixture(apps, schema_editor):
fixture_file = os.path.join(fixture_dir, fixture_filename)
call_command('loaddata', fixture_file)
カスタムディレクトリを使用する場合に便利です。
2.3。 Simplest:app_label
でloaddata
を呼び出すと、<yourapp>
のfixtures
dirからフィクスチャが自動的にロードされます。
from Django.core.management import call_command
fixture = 'initial_data'
def load_fixture(apps, schema_editor):
call_command('loaddata', fixture, app_label='yourapp')
app_label
を指定しない場合、loaddataはallアプリフィクスチャディレクトリからfixture
ファイル名をロードしようとしますt want)。
それを実行します
python manage.py migrate <yourapp>
NOTデータ移行で直接loaddata
管理コマンドを使用する必要があります。
# Bad example for a data migration
from Django.db import migrations
from Django.core.management import call_command
def load_fixture(apps, schema_editor):
# No, it's wrong. DON'T DO THIS!
call_command('loaddata', 'your_data.json', app_label='yourapp')
class Migration(migrations.Migration):
dependencies = [
# Dependencies to other migrations
]
operations = [
migrations.RunPython(load_fixture),
]
loaddata
は、Django.core.serializers.python.Deserializer
を利用します。これは、最新のモデルを使用して、移行の履歴データをデシリアライズします。それは不正な動作です。
たとえば、loaddata
管理コマンドを使用してフィクスチャからデータをロードするデータ移行があり、開発環境にすでに適用されているとします。
後で、新しいrequiredフィールドを対応するモデルに追加することにしました。そのため、それを実行し、更新されたモデルに対して新しい移行を行います(場合によっては、 ./manage.py makemigrations
のプロンプトが表示されたときの新しいフィールドへの1回限りの値)。
次の移行を実行すると、すべて順調です。
最後に、Djangoアプリケーションの開発が完了し、運用サーバーに展開します。これで、実稼働環境で移行全体を最初から実行するときが来ました。
ただし、データの移行は失敗します。これは、現在のコードを表すloaddata
コマンドからの逆シリアル化されたモデルを、新しいrequiredフィールドの空のデータと共に保存できないためです。追加しました。元のフィクスチャには、必要なデータがありません!
ただし、新しいフィールドに必要なデータでフィクスチャを更新しても、データの移行は失敗します。データ移行の実行中、対応する列をデータベースに追加するnext移行はまだ適用されていません。存在しない列にデータを保存することはできません!
結論:データ移行では、loaddata
コマンドはモデルとデータベースの間に潜在的な不整合をもたらします。間違いなくではなくをデータ移行で直接使用してください。
loaddata
コマンドは、Django.core.serializers.python._get_model
関数に依存してフィクスチャから対応するモデルを取得します。これにより、モデルの最新バージョンが返されます。モンキーパッチを適用して、履歴モデルを取得する必要があります。
(次のコードはDjango 1.8.xで機能します)
# Good example for a data migration
from Django.db import migrations
from Django.core.serializers import base, python
from Django.core.management import call_command
def load_fixture(apps, schema_editor):
# Save the old _get_model() function
old_get_model = python._get_model
# Define new _get_model() function here, which utilizes the apps argument to
# get the historical version of a model. This piece of code is directly stolen
# from Django.core.serializers.python._get_model, unchanged. However, here it
# has a different context, specifically, the apps variable.
def _get_model(model_identifier):
try:
return apps.get_model(model_identifier)
except (LookupError, TypeError):
raise base.DeserializationError("Invalid model identifier: '%s'" % model_identifier)
# Replace the _get_model() function on the module, so loaddata can utilize it.
python._get_model = _get_model
try:
# Call loaddata command
call_command('loaddata', 'your_data.json', app_label='yourapp')
finally:
# Restore old _get_model() function
python._get_model = old_get_model
class Migration(migrations.Migration):
dependencies = [
# Dependencies to other migrations
]
operations = [
migrations.RunPython(load_fixture),
]
いくつかのコメント(n__oのコメント)と、複数のアプリに多数のinitial_data.*
ファイルがあるという事実に触発されて、これらの作成を容易にするDjangoアプリを作成することにしましたデータの移行。
Django-migration-fixture を使用すると、次の管理コマンドを実行するだけで、すべてのINSTALLED_APPS
でinitial_data.*
ファイルを検索し、データ移行に変換できます。
./manage.py create_initial_data_fixtures
Migrations for 'eggs':
0002_auto_20150107_0817.py:
Migrations for 'sausage':
Ignoring 'initial_data.yaml' - migration already exists.
Migrations for 'foo':
Ignoring 'initial_data.yaml' - not migrated.
インストール/使用方法については Django-migration-fixture をご覧ください。
移行されたアプリに初期データを読み込む最良の方法は、データの移行を使用することです(ドキュメントでも推奨されています)。利点は、テスト中と生産中の両方でフィクスチャがロードされることです。
@n__oは、移行でloaddata
コマンドを再実装することを提案しました。ただし、私のテストでは、loaddata
コマンドを直接呼び出すことも正常に機能します。したがって、プロセス全体は次のとおりです。
<yourapp>/fixtures/initial_data.json
にフィクスチャファイルを作成します
空の移行を作成します。
python manage.py makemigrations --empty <yourapp>
移行ファイル/migrations/0002_auto_xxx.pyを編集します
from Django.db import migrations
from Django.core.management import call_command
def loadfixture(apps, schema_editor):
call_command('loaddata', 'initial_data.json')
class Migration(migrations.Migration):
dependencies = [
('<yourapp>', '0001_initial'),
]
operations = [
migrations.RunPython(loadfixture),
]
データベースに初期データを提供するには、 data migration。 を記述します。データの移行では、 RunPython 関数を使用してデータをロードします。
この方法は非推奨であるため、loaddataコマンドを記述しないでください。
データの移行は1回のみ実行されます。移行は、移行の順序付けられたシーケンスです。 003_xxxx.py移行が実行されると、Django移行により、このアプリがこれまで(003)まで移行されることがデータベースに書き込まれ、次の移行のみが実行されます。
残念ながら、上記のソリューションはうまくいきませんでした。モデルを変更するたびに、フィクスチャを更新する必要があることがわかりました。理想的には、代わりに作成されたデータとフィクスチャがロードされたデータを同様に変更するデータ移行を記述します。
これを容易にするために クイック関数を書きました これは現在のアプリのfixtures
ディレクトリを見て、フィクスチャをロードします。移行のフィールドに一致するモデル履歴のポイントで、この関数を移行に配置します。
Django 2.1では、いくつかのモデル(国名など)に初期データをロードしたかった。
しかし、初期移行の実行直後にこれが自動的に行われるようにしたかったのです。
したがって、初期データのロードを必要とする各アプリケーション内にsql/
フォルダーがあればいいと思いました。
次に、そのsql/
フォルダー内に、対応するモデルに初期データをロードするために必要なDMLを含む.sql
ファイルがあります。たとえば、
INSERT INTO appName_modelName(fieldName)
VALUES
("country 1"),
("country 2"),
("country 3"),
("country 4");
よりわかりやすくするために、これはsql/
フォルダーを含むアプリの外観です。
また、特定の順序でsql
スクリプトを実行する必要がある場合もありました。そこで、上の画像に見られるように、ファイル名の前に連続番号を付けることにしました。
次に、python manage.py migrate
を実行して、アプリケーションフォルダー内で使用可能なSQLs
を自動的にロードする方法が必要でした。
そこで、initial_data_migrations
という名前の別のアプリケーションを作成し、INSTALLED_APPS
ファイルのsettings.py
のリストにこのアプリを追加しました。次に、migrations
フォルダーを内部に作成し、run_sql_scripts.py
というファイルを追加しました(これは実際にカスタム移行です)。以下の画像に見られるように:
各アプリケーション内で使用可能なすべてのsql
スクリプトを実行するように、run_sql_scripts.py
を作成しました。これは、誰かがpython manage.py migrate
を実行したときに起動されます。このカスタムmigration
は、必要なアプリケーションが0001_initial.py
マイグレーションを実行した後にのみsql
ステートメントの実行を試みるように、関係するアプリケーションも依存関係として追加します(試行したくない存在しないテーブルに対してSQLステートメントを実行する)。
そのスクリプトのソースは次のとおりです。
import os
import itertools
from Django.db import migrations
from YourDjangoProjectName.settings import BASE_DIR, INSTALLED_APPS
SQL_FOLDER = "/sql/"
APP_SQL_FOLDERS = [
(os.path.join(BASE_DIR, app + SQL_FOLDER), app) for app in INSTALLED_APPS
if os.path.isdir(os.path.join(BASE_DIR, app + SQL_FOLDER))
]
SQL_FILES = [
sorted([path + file for file in os.listdir(path) if file.lower().endswith('.sql')])
for path, app in APP_SQL_FOLDERS
]
def load_file(path):
with open(path, 'r') as f:
return f.read()
class Migration(migrations.Migration):
dependencies = [
(app, '__first__') for path, app in APP_SQL_FOLDERS
]
operations = [
migrations.RunSQL(load_file(f)) for f in list(itertools.chain.from_iterable(SQL_FILES))
]
誰かがこれが役立ってくれるといいのですが、私にとってはうまくいったと思います!ご質問がありましたらお知らせください。
注:私はDjangoを使い始めたばかりなので、これは最良の解決策ではないかもしれませんが、それでもこの "How-to"を皆さんと共有したかったのです。これについてグーグルで検索している間はあまり情報が見つかりませんでした。
私の意見では、フィクスチャは少し悪いです。データベースが頻繁に変更される場合、それらを最新の状態に保つのはすぐに悪夢になります。実際には、それは私の意見だけでなく、「Djangoの2つのスクープ」という本の中でずっとよく説明されています。
代わりに、Pythonファイルを作成して、初期セットアップを提供します。さらに何かが必要な場合は、 Factory boy をご覧になることをお勧めします。
一部のデータを移行する必要がある場合は、 data migrations を使用する必要があります。
「フィクスチャの書き込み、モデルファクトリの使用」 フィクスチャの使用についてもあります。