Así que hace aproximadamente un año comencé un proyecto y, como todos los nuevos desarrolladores, realmente no me enfoqué demasiado en la estructura, sin embargo, ahora que estoy más junto con Django, comenzó a parecer que el diseño de mi proyecto, principalmente mis modelos, tienen una estructura horrible. .
Tengo modelos mantenidos principalmente en una sola aplicación y realmente la mayoría de estos modelos deberían estar en sus propias aplicaciones individuales, intenté resolver esto y moverlos hacia el sur, sin embargo, lo encontré complicado y realmente difícil debido a las claves externas, etc.
Sin embargo, debido a Django 1.7 y al soporte integrado para las migraciones, ¿hay una mejor manera de hacerlo ahora?

Respuestas:
Estoy eliminando la respuesta anterior, ya que puede provocar la pérdida de datos. Como mencionó Ozan , podemos crear 2 migraciones, una en cada aplicación. Los comentarios debajo de esta publicación se refieren a mi antigua respuesta.
Primera migración para eliminar el modelo de la primera aplicación.
Edite el archivo de migración para incluir estas operaciones.
Segunda migración que depende de la primera migración y crea la nueva tabla en la segunda aplicación. Después de mover el código del modelo a la segunda aplicación
y edite el archivo de migración a algo como esto.
fuente
./manage.py migratetodo terminará en buen estado. Fingir manualmente las migraciones es una OMI de una manera incorrecta.Esto se puede hacer con bastante facilidad usando
migrations.SeparateDatabaseAndState. Básicamente, utilizamos una operación de base de datos para cambiar el nombre de la tabla simultáneamente con dos operaciones de estado para eliminar el modelo del historial de una aplicación y crearlo en el de otra.Eliminar de la aplicación anterior
En la migración:
Agregar a nueva aplicación
Primero, copie el modelo a la nueva aplicación model.py, luego:
Esto generará una migración con una
CreateModeloperación ingenua como única operación. Envuelva eso en unaSeparateDatabaseAndStateoperación tal que no intentemos recrear la tabla. Incluya también la migración previa como una dependencia:fuente
Encontré el mismo problema. La respuesta de Ozan me ayudó mucho, pero desafortunadamente no fue suficiente. De hecho, tenía varios ForeignKey vinculados al modelo que quería mover. Después de un dolor de cabeza, encontré la solución, así que decidí publicarla para resolver el tiempo de las personas.
Necesita 2 pasos más:
ForeignKeyvinculación aTheModeldentroIntegerfield. Entonces correpython manage.py makemigrationsForeignKey(TheModel)lugar deIntegerField(). Luego realice las migraciones nuevamente (python manage.py makemigrations). Luego puede migrar y debería funcionar (python manage.py migrate)Espero eso ayude. Por supuesto, pruébelo en local antes de intentarlo en producción para evitar malas sorpresas :)
fuente
Cómo lo hice (probado en Django == 1.8, con postgres, así que probablemente también 1.7)
Situación
app1.YourModel
pero quieres que vaya a: app2.YourModel
agregue esto a app2.YourModel:
$ python manage.py makemigrations app2
Se realiza una nueva migración (por ejemplo, 0009_auto_something.py) en app2 con una declaración migrations.CreateModel (), mueva esta declaración a la migración inicial de app2 (por ejemplo, 0001_initial.py) (será como siempre ha estado allí). Y ahora elimine la migración creada = 0009_auto_something.py
Así como actúas, como app2.YourModel siempre ha estado allí, ahora elimina la existencia de app1.YourModel de tus migraciones. Significado: comente las declaraciones CreateModel y cada ajuste o migración de datos que utilizó después de eso.
Y, por supuesto, cada referencia a app1.YourModel debe cambiarse a app2.YourModel a través de su proyecto. Además, no olvide que todas las claves foráneas posibles para app1.YourModel en las migraciones deben cambiarse a app2.YourModel
Ahora, si hace $ python manage.py migrate, nada ha cambiado, también cuando hace $ python manage.py makemigrations, no se ha detectado nada nuevo.
Ahora el toque final: elimine Class Meta de app2.YourModel y haga $ python manage.py makemigrations app2 && python manage.py migrate app2 (si observa esta migración verá algo como esto :)
table = None, significa que tomará el nombre de tabla predeterminado, que en este caso será app2_yourmodel.
PS durante la migración verá que esa content_type app1.yourmodel se ha eliminado y se puede eliminar. Puedes decir sí a eso, pero solo si no lo usas. En caso de que dependa en gran medida de que tenga FK para ese tipo de contenido intacto, no responda sí o no todavía, pero vaya al db esa vez manualmente, elimine el tipo de contenido app2.yourmodel y cambie el nombre de la aplicación contenttype1. yourmodel a app2.yourmodel, y luego continúe respondiendo no.
fuente
app_label = 'app1'opción meta.field1 = models.ForeignKey('app1.myModel').cuando migro, recibo un ValueError que dice esofield1 was declared with a lazy reference to 'app1.myModel' but app 'app1' doesn't provide model 'MyModel'Me ponen nervioso las migraciones de codificación manual (como lo requiere la respuesta de Ozan ), por lo que lo siguiente combina las estrategias de Ozan y Michael para minimizar la cantidad de codificación manual requerida:
makemigrations.app1aapp2Según lo recomendado por @Michael, apuntamos el nuevo modelo a la tabla de base de datos anterior usando la
db_tableopción Meta en el modelo "nuevo":Ejecutar
makemigrations. Esto generaráCreateModelinapp2yDeleteModelinapp1. Técnicamente, estas migraciones se refieren exactamente a la misma tabla y eliminarían (incluidos todos los datos) y volverían a crear la tabla.En realidad, no queremos (o necesitamos) hacer nada a la mesa. Solo necesitamos que Django crea que el cambio se ha realizado. Según la respuesta de @ Ozan, la
state_operationsbanderaSeparateDatabaseAndStatehace esto. Así que envolvemos todas lasmigrationsentradas EN AMBOS ARCHIVOS DE MIGRACIONES conSeparateDatabaseAndState(state_operations=[...]). Por ejemplo,se convierte
También debe asegurarse de que la nueva
CreateModelmigración "virtual" dependa de cualquier migración que realmente haya creado o alterado la tabla original . Por ejemplo, si sus nuevas migraciones sonapp2.migrations.0004_auto_<date>(para elCreate) yapp1.migrations.0007_auto_<date>(para elDelete), lo más simple es:app1.migrations.0007_auto_<date>y copie suapp1dependencia (por ejemplo('app1', '0006...'),). Esta es la migración "inmediatamente anterior"app1y debe incluir dependencias de toda la lógica de construcción del modelo real.app2.migrations.0004_auto_<date>y agregue la dependencia que acaba de copiar a sudependencieslista.Si tiene
ForeignKeyrelación (s) con el modelo que está moviendo, lo anterior puede no funcionar. Esto sucede porque:ForeignKeycambios.ForeignKeycambios, porstate_operationslo que debemos asegurarnos de que estén separados de las operaciones de la tabla.NOTA: Django 2.2 agregó una advertencia (
models.E028) que rompe este método. Es posible que pueda solucionarlo,managed=Falsepero no lo he probado.El conjunto "mínimo" de operaciones difiere según la situación, pero el siguiente procedimiento debería funcionar para la mayoría / todas las
ForeignKeymigraciones:app1aapp2, establezcadb_table, pero NO cambie ninguna referencia de FK.makemigrationsy ajuste toda laapp2migración enstate_operations(ver arriba)app2CreateTablela últimaapp1migraciónmodels.py(NO lo elimine) para que no compita con la clase importada.Ejecuta
makemigrationspero NO envuelvas nadastate_operations(los cambios de FK deberían suceder). Agregue una dependencia en todas lasForeignKeymigraciones (es decirAlterField) a laCreateTablemigración enapp2(necesitará esta lista para el siguiente paso, así que haga un seguimiento de ellas). Por ejemplo:CreateModelegapp2.migrations.0002_auto_<date>y copie el nombre de esa migración.Encuentre todas las migraciones que tengan una ForeignKey para ese modelo (por ejemplo, buscando
app2.YourModelmigraciones como:Agregue la
CreateModelmigración como una dependencia:Eliminar los modelos de
app1makemigrationsy ajuste laapp1migración enstate_operations.ForeignKeymigraciones (es decirAlterField) del paso anterior (puede incluir migraciones enapp1yapp2).DeleteTableya dependía de lasAlterFieldmigraciones, por lo que no necesitaba aplicarlas manualmente (es decir,AlterantesDelete).En este punto, Django está listo. El nuevo modelo apunta a la tabla anterior y las migraciones de Django lo han convencido de que todo se ha reubicado adecuadamente. La gran advertencia (de la respuesta de @ Michael) es que
ContentTypese crea una nueva para el nuevo modelo. Si vincula (p. Ej., PorForeignKey) a tipos de contenido, deberá crear una migración para actualizar laContentTypetabla.Quería limpiar después de mí (opciones de Meta y nombres de tablas), así que utilicé el siguiente procedimiento (de @ Michael):
db_tableentrada Metamakemigrationsnuevamente para generar el cambio de nombre de la base de datosDeleteTablemigración. No parece que deba ser necesario, ya queDeletedebe ser puramente lógico, pero me he encontrado con errores (por ejemploapp1_yourmodel, no existe) si no lo hago.fuente
table_name: (models.E028) db_table 'table_name' is used by multiple models: app1.Model, app2.Model.managed=Falsepero no estoy en ningún lugar para verificarlo.Otra alternativa hacky si los datos no son grandes o demasiado complicados, pero aún son importantes para mantener, es:
fuente
Copiado de mi respuesta en https://stackoverflow.com/a/47392970/8971048
En caso de que necesite mover el modelo y ya no tenga acceso a la aplicación (o no desee el acceso), puede crear una nueva Operación y considerar crear un nuevo modelo solo si el modelo migrado no existe.
En este ejemplo, paso 'MyModel' de old_app a myapp.
fuente
Esto se prueba aproximadamente, ¡así que no olvide hacer una copia de seguridad de su base de datos!
Por ejemplo, hay dos aplicaciones:
src_appydst_app, queremos mover el modeloMoveMedesrc_appadst_app.Cree migraciones vacías para ambas aplicaciones:
Supongamos que las nuevas migraciones son
XXX1_src_app_newyXXX1_dst_app_new, previamente, las principales migraciones sonXXX0_src_app_oldyXXX0_dst_app_old.Agregue una operación que
MoveMecambie el nombre de la tabla para el modelo y cambie el nombre de su app_label en ProjectState aXXX1_dst_app_new. No olvides agregar dependencia a laXXX0_src_app_oldmigración. LaXXX1_dst_app_newmigración resultante es:Agregar dependencia
XXX1_dst_app_newaXXX1_src_app_new.XXX1_src_app_newes una migración sin operación que se necesita para garantizar que las futurassrc_appmigraciones se ejecuten despuésXXX1_dst_app_new.Pasar
MoveMedesrc_app/models.pyadst_app/models.py. Entonces corre:¡Eso es todo!
fuente
Puedes probar lo siguiente (no probado):
src_appadest_appdest_app; asegúrese de que la migración del esquema dependa de la últimasrc_appmigración ( https://docs.djangoproject.com/en/dev/topics/migrations/#migration-files )dest_app, que copia todos los datos desrc_appsrc_app; asegúrese de que la migración del esquema depende de la última migración (de datos) dedest_app, es decir, la migración del paso 3Tenga en cuenta que copiará toda la tabla, en lugar de moverla , pero de esa manera ambas aplicaciones no tienen que tocar una tabla que pertenece a la otra aplicación, lo que creo que es más importante.
fuente
Digamos que está moviendo el modelo TheModel de app_a a app_b.
Una solución alternativa es alterar las migraciones existentes a mano. La idea es que cada vez que vea una operación que altere TheModel en las migraciones de app_a, copie esa operación al final de la migración inicial de app_b. Y cada vez que vea una referencia 'app_a.TheModel' en las migraciones de app_a, la cambia a 'app_b.TheModel'.
Acabo de hacer esto para un proyecto existente, donde quería extraer cierto modelo a una aplicación reutilizable. El procedimiento fue sin problemas. Supongo que las cosas serían mucho más difíciles si hubiera referencias de app_b a app_a. Además, tenía una Meta.db_table definida manualmente para mi modelo que podría haber ayudado.
En particular, terminarás con un historial de migración alterado. Esto no importa, incluso si tiene una base de datos con las migraciones originales aplicadas. Si tanto las migraciones originales como las reescritas terminan con el mismo esquema de base de datos, dicha reescritura debería estar bien.
fuente
Haga esto individualmente para cada modelo que necesite ser movido. No sugeriría hacer lo que dice la otra respuesta al cambiar a números enteros y volver a claves externas Existe la posibilidad de que las nuevas claves externas sean diferentes y las filas puedan tener diferentes ID después de las migraciones y no quería correr ningún riesgo de identificadores no coincidentes al volver a las claves externas.
fuente