Загрузка исходных данных с помощью Django 1.7 и миграции данных
недавно я переключился с Django 1.6 на 1.7, и я начал использовать миграции (я никогда не использовал Юг).
до 1.7, я использовал для загрузки исходных данных с fixture/initial_data.json файл, который был загружен с (при создании базы данных).
теперь я начал использовать миграции, и это поведение устарело:
если приложение использует миграции, нет автоматической загрузки светильников.
Поскольку миграция будет необходима для приложения в Django 2.0 такое поведение считается устаревшим. Если вы хотите загрузить исходные данные для приложения, рассмотрите возможность сделать это в миграции данных.
(https://docs.djangoproject.com/en/1.7/howto/initial-data/#automatically-loading-initial-data-fixtures)
The официальная документация не имеет четкого примера о том, как это сделать, поэтому мой вопрос:
каков наилучший способ импорта таких исходных данных с использованием данных миграции:
- написать код Python с несколькими вызовами
mymodel.create(...), - используйте или напишите функцию Django (например, вызов
loaddata) для загрузки данных из файла JSON fixture.
Я предпочитаю второй вариант.
Я не хочу использовать юг, так как Джанго, кажется, может сделать это изначально сейчас.
7 ответов:
обновление: см. комментарий @GwynBleidD ниже для проблем, которые может вызвать это решение, и см. ответ @Rockallite ниже для подхода, который более долговечен для будущих изменений модели.
предполагая, что у вас есть файл крепления в
<yourapp>/fixtures/initial_data.json
создайте пустую миграцию:
В Django 1.7:
python manage.py makemigrations --empty <yourapp>в Django 1.8+, вы можете обеспечить a имя:
python manage.py makemigrations --empty <yourapp> --name load_intial_dataредактировать файл миграции
<yourapp>/migrations/0002_auto_xxx.py2.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Сapp_labelзагрузит светильники из<yourapp>' sfixturesреж автоматически :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команда вводит потенциальное несоответствие между моделью и базой данных. Вы должны обязательно не используйте его непосредственно в a миграция данных.Решение
использует
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), ]
чтобы дать вашей базе данных некоторые исходные данные, напишите миграция данных. При переносе данных используйте RunPython функция для загрузки данных.
Не пишите никаких команд loaddata, так как этот способ устарел.
перенос данных будет выполняться только один раз. Миграции представляют собой упорядоченную последовательность миграций. Когда 003_xxxx.py выполняется миграция, Django migrations записывает в базу данных, что это приложение переносится до этого (003), и будет выполняться только следующие миграции.
приведенные выше решения не работают для меня, к сожалению. Я обнаружил, что каждый раз, когда я меняю свои модели, я должен обновлять свои светильники. В идеале я бы вместо этого написал миграции данных для изменения созданных данных и данных, загруженных приспособлением, аналогично.
чтобы облегчить это Я написал быструю функцию которая в
fixturesкаталог текущего приложения и загрузить приспособление. Поместите эту функцию в миграцию в точке истории модели, которая соответствует поля в миграции.
на мой взгляд, светильники немного плохие. Если ваша база данных часто меняется, поддержание их в актуальном состоянии скоро станет кошмаром. На самом деле, это не только мое мнение, в книге "два совка Джанго" это объясняется гораздо лучше.
вместо этого я напишу файл Python для обеспечения начальной установки. Если вам нужно что-то еще, я предлагаю вам посмотреть на Фабрика мальчик.
Если вам нужно перенести некоторые данные, вы должны использовать сведения миграции.
там же "Сожгите Ваши Приспособления, Используйте Модельные Фабрики" об использовании приспособлений.
Comments