최근에 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 코드 작성- Django 함수 ( 예 : 호출
loaddata
)를 사용하거나 작성 하여 JSON 픽스처 파일에서 데이터를로드합니다.
두 번째 옵션을 선호합니다.
Django가 이제 기본적으로 할 수있는 것처럼 보이기 때문에 South를 사용하고 싶지 않습니다.
답변
업데이트 :이 솔루션으로 인해 발생할 수있는 문제에 대해서는 아래 @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. 가장 간단한 방법 :
loaddata
withapp_label
를 호출 하면<yourapp>
의fixtures
디렉토리 에서 조명기가 자동으로 로드 됩니다.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는 모든 앱 조명기 디렉터리fixture
에서 파일 이름 을로드하려고 시도합니다 (원하지 않을 수도 있음). -
실행
python manage.py migrate <yourapp>
답변
짧은 버전
당신은해야 하지 사용하는 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
관리 명령을 사용하여 고정 장치에서 데이터를로드 하는 데이터 마이그레이션이 있고 이미 개발 환경에 적용되어 있다고 가정합니다.
나중에 해당 모델에 새 필수 필드 를 추가하기로 결정하고 이를 수행하고 업데이트 된 모델에 대해 새 마이그레이션을 수행합니다 ( ./manage.py makemigrations
메시지가 표시 될 때 새 필드에 일회성 값을 제공 할 수 있음).
다음 마이그레이션을 실행하면 모든 것이 잘됩니다.
마지막으로 Django 애플리케이션 개발을 완료하고 프로덕션 서버에 배포합니다. 이제 프로덕션 환경에서 처음부터 전체 마이그레이션을 실행할 때입니다.
그러나 데이터 마이그레이션은 실패합니다 . loaddata
현재 코드를 나타내는 명령 의 역 직렬화 된 모델 을 추가 한 새 필수 필드에 대해 빈 데이터로 저장할 수 없기 때문 입니다. 원래 조명기에는 필요한 데이터가 없습니다!
그러나 새 필드에 필요한 데이터로 설비를 업데이트하더라도 데이터 마이그레이션은 여전히 실패합니다 . 데이터 마이그레이션이 실행 중일 때 해당 컬럼을 데이터베이스에 추가하는 다음 마이그레이션은 아직 적용되지 않습니다. 존재하지 않는 열에는 데이터를 저장할 수 없습니다!
결론 : 데이터 마이그레이션에서이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 ‘s)과 initial_data.*
여러 앱에 많은 파일이 분산되어 있다는 사실에 영감을 받아 이러한 데이터 마이그레이션 생성을 용이하게하는 Django 앱을 만들기로 결정했습니다.
사용 장고 – 마이그레이션 – 고정은 간단히 다음과 같은 관리 명령을 실행할 수 있으며, 그것은 당신의 모든 통해 검색합니다 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 를 참조하십시오 .
답변
데이터베이스에 초기 데이터를 제공하려면 데이터 마이그레이션을 작성하십시오 .
데이터 마이그레이션에서 RunPython 함수를 사용하여 데이터를로드합니다.
이 방법은 더 이상 사용되지 않으므로 loaddata 명령을 작성하지 마십시오.
데이터 마이그레이션은 한 번만 실행됩니다. 마이그레이션은 순서가 지정된 마이그레이션 시퀀스입니다. 003_xxxx.py 마이그레이션이 실행되면 django 마이그레이션은이 앱이이 앱이 마이그레이션 될 때까지 (003) 마이그레이션되었음을 데이터베이스에 기록하고 다음 마이그레이션 만 실행합니다.
답변
위에 제시된 솔루션은 불행히도 저에게 효과적이지 않았습니다. 모델을 변경할 때마다 조명기를 업데이트해야한다는 것을 알았습니다. 이상적으로는 데이터 마이그레이션을 작성하여 생성 된 데이터와 고정 장치로드 데이터를 비슷하게 수정합니다.
이를 용이하게하기 위해 현재 앱 의 디렉토리를 살펴보고 조명기를로드하는 빠른 함수 를 작성했습니다fixtures
. 마이그레이션의 필드와 일치하는 모델 히스토리 지점에서이 기능을 마이그레이션에 넣으십시오.
답변
제 생각에는 비품이 조금 나쁩니다. 데이터베이스가 자주 변경되면 최신 상태로 유지하는 것이 곧 악몽이 될 것입니다. 사실, 그것은 내 의견뿐만 아니라 “장고의 두 가지 특종”이라는 책에서 훨씬 더 잘 설명되어 있습니다.
대신 초기 설정을 제공하기 위해 Python 파일을 작성하겠습니다. 더 필요한 것이 있으면 Factory boy 를 보도록 권합니다 .
일부 데이터를 마이그레이션해야하는 경우 데이터 마이그레이션을 사용해야 합니다 .
도있다 “당신의 설비, 사용 모델 공장을 굽기” 기구를 사용하는 방법에 대해.
답변
Django 2.1에서는 초기 데이터로 일부 모델 (예 : 국가 이름 등)을로드하고 싶었습니다.
그러나 나는 이것이 초기 마이그레이션 실행 직후 자동으로 발생하기를 원했습니다.
그래서 sql/
각 애플리케이션 안에 초기 데이터를로드해야하는 폴더 가 있으면 좋겠다고 생각했습니다 .
그런 다음 해당 sql/
폴더 내에 .sql
초기 데이터를 해당 모델에로드하는 데 필요한 DML 이있는 파일 이 있습니다 . 예를 들면 다음과 같습니다.
INSERT INTO appName_modelName(fieldName)
VALUES
("country 1"),
("country 2"),
("country 3"),
("country 4");
좀 더 설명하기 위해 sql/
폴더가 포함 된 앱의 모습은 다음과 같습니다.
또한 sql
특정 순서로 스크립트를 실행 해야하는 경우도 발견했습니다 . 그래서 위의 이미지에서 볼 수 있듯이 파일 이름 앞에 연속 번호를 붙이기로 결정했습니다.
그런 다음로드 할 방법이 필요했습니다. SQLs
을 수행하여 응용 프로그램 폴더 내에서 사용 가능한 모든 것을 자동으로python manage.py migrate
.
그래서 이름이 다른 응용 프로그램을 만든 initial_data_migrations
후, 나는 목록에이 응용 프로그램을 추가 INSTALLED_APPS
에서 settings.py
파일. 그런 다음 migrations
내부 에 폴더를 만들고 run_sql_scripts.py
( 실제로는 사용자 지정 마이그레이션 ) 이라는 파일을 추가했습니다 . 아래 이미지에서 볼 수 있듯이 :
각 응용 프로그램 내에서 사용 가능한 run_sql_scripts.py
모든 sql
스크립트 를 실행하도록 만들었습니다 . 이것은 누군가가 실행되면 해고됩니다 python manage.py migrate
. 이 사용자 지정 migration
은 또한 관련 응용 프로그램을 종속성으로 추가하여 sql
필요한 응용 프로그램이 0001_initial.py
마이그레이션 을 실행 한 후에 만 명령문 을 실행하려고 시도 합니다 (존재하지 않는 테이블에 대해 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를 시작하기 때문에 이것이 최선의 해결책이 아닐 수도 있지만, 인터넷 검색을하는 동안 많은 정보를 찾지 못했기 때문에이 “방법”을 모두와 공유하고 싶었습니다.