Hola, hallamos la respuesta a lo que andabas buscando, continúa leyendo y la obtendrás a continuación.
Solución:
Actualizar: Vea el comentario de @ GwynBleidD a continuación para conocer los problemas que esta solución puede causar, y vea la respuesta de @ Rockallite a continuación para obtener un enfoque que es más duradero para futuros cambios de modelo.
Suponiendo que tiene un archivo de accesorio en
-
Crea tu migración vacía:
En Django 1.7:
python manage.py makemigrations --empty
En Django 1.8+, puede proporcionar un nombre:
python manage.py makemigrations --empty
--name load_intial_data -
Edite su archivo de migración
/migrations/0002_auto_xxx.py 2.1. Implementación personalizada, inspirada en Django ‘
loaddata
(respuesta inicial):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. Una solución más sencilla para
load_fixture
(por sugerencia de @ 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)
Útil si desea utilizar un directorio personalizado.
2.3. Más simple: vocación
loaddata
conapp_label
cargará accesorios desde el
‘sfixtures
dir automáticamente:from django.core.management import call_command fixture = 'initial_data' def load_fixture(apps, schema_editor): call_command('loaddata', fixture, app_label='yourapp')
Si no especifica
app_label
, loaddata intentará cargarfixture
nombre de archivo de todos directorios de accesorios de aplicaciones (que probablemente no quieras). -
Ejecutarlo
python manage.py migrate
Version corta
Debería NO usar loaddata
comando de gestión directamente en una migración de datos.
# 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),
]
Versión larga
loaddata
utiliza django.core.serializers.python.Deserializer
que utiliza los modelos más actualizados para deserializar datos históricos en una migración. Ese es un comportamiento incorrecto.
Por ejemplo, supongamos que hay una migración de datos que utiliza loaddata
comando de administración para cargar datos desde un dispositivo, y ya está aplicado en su entorno de desarrollo.
Más tarde, decide agregar un nuevo requerido campo al modelo correspondiente, así que lo hace y realiza una nueva migración contra su modelo actualizado (y posiblemente proporcione un valor único al nuevo campo cuando ./manage.py makemigrations
le pide).
Ejecuta la siguiente migración y todo está bien.
Finalmente, ha terminado de desarrollar su aplicación Django y la implementa en el servidor de producción. Ahora es el momento de ejecutar todas las migraciones desde cero en el entorno de producción.
Sin embargo, la migración de datos falla. Eso es porque el modelo deserializado de loaddata
comando, que representa el código actual, no se puede guardar con datos vacíos para el nuevo requerido campo que agregó. ¡El aparato original carece de los datos necesarios para ello!
Pero incluso si actualiza el dispositivo con los datos requeridos para el nuevo campo, la migración de datos aún falla. Cuando se está ejecutando la migración de datos, Siguiente la migración que agrega la columna correspondiente a la base de datos, aún no se aplica. ¡No puede guardar datos en una columna que no existe!
Conclusión: en una migración de datos, el loaddata
El comando introduce una posible inconsistencia entre el modelo y la base de datos. Definitivamente deberías NO utilícelo directamente en una migración de datos.
La solución
loaddata
el comando se basa en django.core.serializers.python._get_model
función para obtener el modelo correspondiente de un dispositivo, que devolverá la versión más actualizada de un modelo. Necesitamos parchearlo para que obtenga el modelo histórico.
(El siguiente código funciona para 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),
]
Inspirado por algunos de los comentarios (es decir, n__o) y el hecho de que tengo muchos initial_data.*
archivos distribuidos en varias aplicaciones, decidí crear una aplicación Django que facilitaría la creación de estas migraciones de datos.
Usando django-migration-fixture, simplemente puede ejecutar el siguiente comando de administración y buscará en todos sus INSTALLED_APPS
por initial_data.*
archivos y convertirlos en migraciones de datos.
./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.
Consulte django-migration-fixture para obtener instrucciones de instalación / uso.
valoraciones y reseñas
Si te ha sido de utilidad este post, nos gustaría que lo compartas con otros programadores de este modo nos ayudas a difundir nuestra información.