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?
- Probablemente podría hacer una migración de datos, pero parece bastante complicado.
- 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. - ¿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
.