Recientemente cambié de Django 1.6 a 1.7 y comencé a usar migraciones (nunca usé South).
Antes de 1.7, solía cargar los datos iniciales con un fixture/initial_data.json
archivo, que se cargaba con el python manage.py syncdb
comando (al crear la base de datos).
Ahora, comencé a usar migraciones y este comportamiento está en desuso:
Si una aplicación usa migraciones, no hay carga automática de accesorios. Dado que se requerirán migraciones para las aplicaciones en Django 2.0, este comportamiento se considera obsoleto. Si desea cargar datos iniciales para una aplicación, considere hacerlo en una migración de datos. ( https://docs.djangoproject.com/en/1.7/howto/initial-data/#automatically-loading-initial-data-fixtures )
La documentación oficial no tiene un ejemplo claro de cómo hacerlo, por eso mi pregunta es:
¿Cuál es la mejor manera de importar dichos datos iniciales mediante migraciones de datos?
- Escriba código Python con múltiples llamadas a
mymodel.create(...)
, - Use o escriba una función de Django ( como llamar
loaddata
) para cargar datos desde un archivo de accesorio JSON.
Prefiero la segunda opción.
No quiero usar South, ya que Django parece poder hacerlo de forma nativa ahora.
Respuestas:
Actualización : 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
<yourapp>/fixtures/initial_data.json
Crea tu migración vacía:
En Django 1.7:
En Django 1.8+, puede proporcionar un nombre:
Edite su archivo de migración
<yourapp>/migrations/0002_auto_xxx.py
2.1. Implementación personalizada, inspirada en Django '
loaddata
(respuesta inicial):2.2. Una solución más simple para
load_fixture
(según la sugerencia de @ juliocesar):Útil si desea utilizar un directorio personalizado.
2.3. Más simple: llamar
loaddata
conapp_label
cargará los dispositivos desde el directorio del<yourapp>
'fixtures
automáticamente:Si no lo especifica
app_label
, loaddata intentará cargar elfixture
nombre de archivo de todos los directorios de accesorios de las aplicaciones (lo que probablemente no desee).Ejecutarlo
fuente
loaddata('loaddata', fixture_filename, app_label='<yourapp>')
también irán directamente al directorio de dispositivos de la aplicación (por lo tanto, no es necesario crear la ruta completa del dispositivo)models.py
archivos actuales , que pueden tener algunos campos adicionales o algunos otros cambios. Si se realizaron algunos cambios después de crear la migración, fallará (por lo que ni siquiera podemos crear migraciones de esquemas después de esa migración). Para solucionarlo, podemos cambiar temporalmente el registro de aplicaciones en el que está trabajando el serializador en el registro proporcionado a la función de migración en el primer parámetro. El registro a la ruta se encuentra endjango.core.serializers.python.apps
.app registry
, sin cambiar una variable global (que podría causar problemas en un futuro hipotético con migraciones de bases de datos paralelas)?Version corta
NO debe usar el
loaddata
comando de administración directamente en una migración de datos.Versión larga
loaddata
utiliza eldjango.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, suponga que hay una migración de datos que utiliza
loaddata
un 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 campo obligatorio 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 se le
./manage.py makemigrations
solicite).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 que ejecute 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 campo obligatorio que agregó. ¡El aparato original carece de los datos necesarios para ello!Pero incluso si actualiza el dispositivo con los datos necesarios para el nuevo campo, la migración de datos aún falla . Cuando se está ejecutando la migración de datos, la siguiente 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
comando introduce una posible inconsistencia entre el modelo y la base de datos. Definitivamente NO debeusarlo directamente en una migración de datos.La solución
loaddata
El comando se basa en ladjango.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)
fuente
objects = serializers.deserialize('json', fixture, ignorenonexistent=True)
sufriría el mismo problema queloaddata
? ¿Oignorenonexistent=True
cubre todos los problemas posibles?ignorenonexistent=True
argumento tiene dos efectos: 1) ignora los modelos de un dispositivo que no están en las definiciones de modelo más actuales, 2) ignora los campos de un modelo de un dispositivo que no están en la definición de modelo correspondiente más actual. Ninguno de ellos maneja la situación del nuevo campo requerido en el modelo . Entonces, sí, creo que sufre el mismo problema que el simpleloaddata
.natural_key()
, que este método no parece admitir; simplemente reemplacé el valor natural_key con la identificación real del modelo referenciado.Inspirado por algunos de los comentarios (es decir, n__o) y el hecho de que tengo muchos
initial_data.*
archivos repartidos en varias aplicaciones, decidí crear una aplicación Django que facilitaría la creación de estas migraciones de datos.Usando Django-migración-accesorio simplemente puede ejecutar el siguiente comando de gestión y se buscará a través de toda su
INSTALLED_APPS
deinitial_data.*
archivos y convertirlos en migraciones de datos.Consulte django-migration-fixture para obtener instrucciones de instalación / uso.
fuente
Para darle a su base de datos algunos datos iniciales, escriba una migración de datos. En la migración de datos, use la función RunPython para cargar sus datos.
No escriba ningún comando loaddata, ya que esta forma está obsoleta.
Sus migraciones de datos se ejecutarán solo una vez. Las migraciones son una secuencia ordenada de migraciones. Cuando se ejecutan las migraciones 003_xxxx.py, django migrations escribe en la base de datos que esta aplicación se migra hasta esta (003), y solo ejecutará las siguientes migraciones.
fuente
myModel.create(...)
(o usar un bucle) en la función RunPython?Lamentablemente, las soluciones presentadas anteriormente no funcionaron para mí. Descubrí que cada vez que cambio mis modelos tengo que actualizar mis luminarias. Idealmente, escribiría migraciones de datos para modificar los datos creados y los datos cargados de dispositivos de manera similar.
Para facilitar esto , escribí una función rápida que buscará en el
fixtures
directorio de la aplicación actual y cargará un dispositivo. Coloque esta función en una migración en el punto del historial del modelo que coincida con los campos de la migración.fuente
RunPython(load_fixture('badger', 'stoat'))
. gist.github.com/danni/1b2a0078e998ac080111En mi opinión, los partidos son un poco malos. Si su base de datos cambia con frecuencia, mantenerla actualizada pronto será una pesadilla. En realidad, no es solo mi opinión, en el libro "Two Scoops of Django" se explica mucho mejor.
En su lugar, escribiré un archivo Python para proporcionar la configuración inicial. Si necesitas algo más, te sugiero que mires al chico de la fábrica .
Si necesita migrar algunos datos, debe usar migraciones de datos .
También hay "Grabe sus dispositivos, use fábricas de modelos" sobre el uso de dispositivos.
fuente
En Django 2.1, quería cargar algunos modelos (como nombres de países, por ejemplo) con datos iniciales.
Pero quería que esto sucediera automáticamente justo después de la ejecución de las migraciones iniciales.
Así que pensé que sería genial tener una
sql/
carpeta dentro de cada aplicación que requiriera que se carguen los datos iniciales.Luego, dentro de esa
sql/
carpeta, tendría.sql
archivos con los DML requeridos para cargar los datos iniciales en los modelos correspondientes, por ejemplo:Para ser más descriptivo, así es como
sql/
se vería una aplicación que contiene una carpeta:También encontré algunos casos en los que necesitaba que los
sql
scripts se ejecutaran en un orden específico. Así que decidí prefijar los nombres de los archivos con un número consecutivo como se ve en la imagen de arriba.Entonces necesitaba una forma de cargar cualquier
SQLs
disponible dentro de cualquier carpeta de aplicación automáticamente al hacerlopython manage.py migrate
.Así que creé otra aplicación llamada
initial_data_migrations
y luego añadí esta aplicación a la lista deINSTALLED_APPS
ensettings.py
archivo. Luego creé unamigrations
carpeta dentro y agregué un archivo llamadorun_sql_scripts.py
( que en realidad es una migración personalizada ). Como se ve en la siguiente imagen:Creé
run_sql_scripts.py
para que se encargue de ejecutar todos lossql
scripts disponibles dentro de cada aplicación. Este luego es despedido cuando alguien correpython manage.py migrate
. Esta costumbremigration
también agrega las aplicaciones involucradas como dependencias, de esa manera intenta ejecutar lassql
declaraciones solo después de que las aplicaciones requeridas hayan ejecutado sus0001_initial.py
migraciones (no queremos intentar ejecutar una declaración SQL contra una tabla que no existe).Aquí está la fuente de ese script:
Espero que alguien lo encuentre útil, ¡funcionó bien para mí !. Si tiene alguna pregunta, por favor hágamelo saber.
NOTA: Puede que esta no sea la mejor solución ya que recién estoy comenzando con django, sin embargo, todavía quería compartir este "Cómo" con todos ustedes ya que no encontré mucha información mientras buscaba en Google sobre esto.
fuente