python django model rename django-south

python - La forma más fácil de cambiar el nombre de un modelo usando Django/South?



rename django-south (4)

He estado buscando una respuesta a esto en el sitio de South, Google y SO, pero no he podido encontrar una forma sencilla de hacerlo.

Quiero cambiar el nombre de un modelo de Django utilizando South. Digamos que tienes lo siguiente:

class Foo(models.Model): name = models.CharField() class FooTwo(models.Model): name = models.CharField() foo = models.ForeignKey(Foo)

y quieres convertir Foo a Bar, a saber

class Bar(models.Model): name = models.CharField() class FooTwo(models.Model): name = models.CharField() foo = models.ForeignKey(Bar)

Para simplificar, solo trato de cambiar el nombre de Foo a Bar , pero ignoro al miembro foo en FooTwo por el momento.

¿Cuál es la forma más fácil de hacer esto con el sur?

  1. Probablemente podría hacer una migración de datos, pero parece bastante complicado.
  2. Escriba una migración personalizada, por ejemplo, db.rename_table(''city_citystate'', ''geo_citystate'') , pero no estoy seguro de cómo arreglar la clave externa en este caso.
  3. ¿Una forma más fácil de saber?

Para responder a su primera pregunta, el simple cambio de modelo / tabla es bastante sencillo. Ejecute el comando:

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(Actualización 2: pruebe --auto lugar de --empty para evitar la advertencia a continuación. Gracias a @KFB por la sugerencia).

Si está usando una versión anterior de south, necesitará startmigration lugar de schemamigration .

A continuación, edite manualmente el archivo de migración para que se vea así:

class Migration(SchemaMigration): def forwards(self, orm): db.rename_table(''yourapp_foo'', ''yourapp_bar'') def backwards(self, orm): db.rename_table(''yourapp_bar'',''yourapp_foo'')

Puedes lograr esto más simplemente usando la opción Meta db_table en tu clase de modelo. Pero cada vez que haces eso, aumentas el peso heredado de tu base de código: si los nombres de las clases difieren de los nombres de las tablas, tu código será más difícil de comprender y mantener. Apoyo totalmente hacer refactorizaciones simples como esta por el bien de la claridad.

(actualización) Acabo de probar esto en producción, y recibí una advertencia extraña cuando fui a aplicar la migración. Decía:

The following content types are stale and need to be deleted: yourapp | foo Any objects related to these content types by a foreign key will also be deleted. Are you sure you want to delete these content types? If you''re unsure, answer ''no''.

Respondí "no" y todo parecía estar bien.


Realice los cambios en models.py y luego ejecute

./manage.py schemamigration --auto myapp

Cuando inspecciona el archivo de migración, verá que elimina una tabla y crea una nueva

class Migration(SchemaMigration): def forwards(self, orm): # Deleting model ''Foo'' db.delete_table(''myapp_foo'') # Adding model ''Bar'' db.create_table(''myapp_bar'', ( ... )) db.send_create_signal(''myapp'', [''Bar'']) def backwards(self, orm): ...

Esto no es exactamente lo que quieres. En su lugar, edite la migración para que se vea así:

class Migration(SchemaMigration): def forwards(self, orm): # Renaming model from ''Foo'' to ''Bar'' db.rename_table(''myapp_foo'', ''myapp_bar'') if not db.dry_run: orm[''contenttypes.contenttype''].objects.filter( app_label=''myapp'', model=''foo'').update(model=''bar'') def backwards(self, orm): # Renaming model from ''Bar'' to ''Foo'' db.rename_table(''myapp_bar'', ''myapp_foo'') if not db.dry_run: orm[''contenttypes.contenttype''].objects.filter(app_label=''myapp'', model=''bar'').update(model=''foo'')

En ausencia de la declaración de update , la llamada db.send_create_signal creará un nuevo ContentType con el nuevo nombre de modelo. Pero es mejor update el ContentType que ya tienes en caso de que haya objetos de la base de datos apuntando a él (por ejemplo, a través de GenericForeignKey ).

Además, si ha cambiado el nombre de algunas columnas que son claves externas al modelo renombrado, no se olvide de

db.rename_column(myapp_model, foo_id, bar_id)


Seguí la solución de Leopd arriba. Pero eso no cambió los nombres de los modelos. Lo cambié manualmente en el código (también en modelos relacionados donde esto se conoce como FK). Y realizó otra migración hacia el sur, pero con la opción --fake. Esto hace que los nombres de los modelos y las tablas sean los mismos.

Como me di cuenta, uno podría comenzar cambiando los nombres de los modelos, luego edite el archivo de migraciones antes de aplicarlos. Mucho más limpio.


South no puede hacerlo solo, ¿cómo sabe que Bar representa lo que Foo solía? Este es el tipo de cosa para la que escribiría una migración personalizada. Puede cambiar su clave ForeignKey en el código como lo hizo anteriormente, y luego es solo el caso de cambiar el nombre de los campos y tablas apropiados, que puede hacer de la manera que desee.

Finalmente, ¿realmente necesitas hacer esto? Todavía tengo que cambiar el nombre de los modelos; los nombres de los modelos son solo un detalle de la implementación, especialmente dada la disponibilidad de la opción de nombre verbose_name .