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 migrate
todo 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
CreateModel
operación ingenua como única operación. Envuelva eso en unaSeparateDatabaseAndState
operació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:
ForeignKey
vinculación aTheModel
dentroIntegerfield
. Entonces correpython manage.py makemigrations
ForeignKey(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
.app1
aapp2
Según lo recomendado por @Michael, apuntamos el nuevo modelo a la tabla de base de datos anterior usando la
db_table
opción Meta en el modelo "nuevo":Ejecutar
makemigrations
. Esto generaráCreateModel
inapp2
yDeleteModel
inapp1
. 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_operations
banderaSeparateDatabaseAndState
hace esto. Así que envolvemos todas lasmigrations
entradas EN AMBOS ARCHIVOS DE MIGRACIONES conSeparateDatabaseAndState(state_operations=[...])
. Por ejemplo,se convierte
También debe asegurarse de que la nueva
CreateModel
migració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 suapp1
dependencia (por ejemplo('app1', '0006...'),
). Esta es la migración "inmediatamente anterior"app1
y 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 sudependencies
lista.Si tiene
ForeignKey
relación (s) con el modelo que está moviendo, lo anterior puede no funcionar. Esto sucede porque:ForeignKey
cambios.ForeignKey
cambios, porstate_operations
lo 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=False
pero 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
ForeignKey
migraciones:app1
aapp2
, establezcadb_table
, pero NO cambie ninguna referencia de FK.makemigrations
y ajuste toda laapp2
migración enstate_operations
(ver arriba)app2
CreateTable
la últimaapp1
migraciónmodels.py
(NO lo elimine) para que no compita con la clase importada.Ejecuta
makemigrations
pero NO envuelvas nadastate_operations
(los cambios de FK deberían suceder). Agregue una dependencia en todas lasForeignKey
migraciones (es decirAlterField
) a laCreateTable
migración enapp2
(necesitará esta lista para el siguiente paso, así que haga un seguimiento de ellas). Por ejemplo:CreateModel
egapp2.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.YourModel
migraciones como:Agregue la
CreateModel
migración como una dependencia:Eliminar los modelos de
app1
makemigrations
y ajuste laapp1
migración enstate_operations
.ForeignKey
migraciones (es decirAlterField
) del paso anterior (puede incluir migraciones enapp1
yapp2
).DeleteTable
ya dependía de lasAlterField
migraciones, por lo que no necesitaba aplicarlas manualmente (es decir,Alter
antesDelete
).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
ContentType
se crea una nueva para el nuevo modelo. Si vincula (p. Ej., PorForeignKey
) a tipos de contenido, deberá crear una migración para actualizar laContentType
tabla.Quería limpiar después de mí (opciones de Meta y nombres de tablas), así que utilicé el siguiente procedimiento (de @ Michael):
db_table
entrada Metamakemigrations
nuevamente para generar el cambio de nombre de la base de datosDeleteTable
migración. No parece que deba ser necesario, ya queDelete
debe 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=False
pero 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_app
ydst_app
, queremos mover el modeloMoveMe
desrc_app
adst_app
.Cree migraciones vacías para ambas aplicaciones:
Supongamos que las nuevas migraciones son
XXX1_src_app_new
yXXX1_dst_app_new
, previamente, las principales migraciones sonXXX0_src_app_old
yXXX0_dst_app_old
.Agregue una operación que
MoveMe
cambie 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_old
migración. LaXXX1_dst_app_new
migración resultante es:Agregar dependencia
XXX1_dst_app_new
aXXX1_src_app_new
.XXX1_src_app_new
es una migración sin operación que se necesita para garantizar que las futurassrc_app
migraciones se ejecuten despuésXXX1_dst_app_new
.Pasar
MoveMe
desrc_app/models.py
adst_app/models.py
. Entonces corre:¡Eso es todo!
fuente
Puedes probar lo siguiente (no probado):
src_app
adest_app
dest_app
; asegúrese de que la migración del esquema dependa de la últimasrc_app
migración ( https://docs.djangoproject.com/en/dev/topics/migrations/#migration-files )dest_app
, que copia todos los datos desrc_app
src_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