¿La forma más fácil de cambiar el nombre de un modelo con Django / South?

141

He estado buscando una respuesta a esto en el sitio de South, Google y SO, pero no pude encontrar una manera simple de hacerlo.

Quiero cambiar el nombre de un modelo de Django usando South. Digamos que tiene 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 simplificarlo, solo estoy tratando de cambiar el nombre de Fooa Bar, pero ignoro al foomiembro FooTwopor ahora.

¿Cuál es la forma más fácil de hacer esto usando South?

  1. Probablemente podría hacer una migración de datos, pero eso 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 solucionar la clave externa en este caso.
  3. ¿Una forma más fácil que sabes?
Vaughnkoch
fuente
55
Consulte también stackoverflow.com/questions/3235995/… para cambiar el nombre de un campo de modelo en lugar de un modelo .
Caracol mecánico
Solución optimizada para Django> = 1.8 stackoverflow.com/questions/25091130/…
Programador químico

Respuestas:

130

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

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(Actualización 2: intente en --autolugar de --emptyevitar la advertencia a continuación. Gracias a @KFB por el consejo).

Si está utilizando una versión anterior de south, necesitará en startmigrationlugar de schemamigration.

Luego 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')   

Puede lograr esto más simplemente usando la db_tableopción Meta en su clase de modelo. Pero cada vez que hace eso, aumenta el peso heredado de su base de código: tener nombres de clase diferentes de los nombres de tabla hace que su código sea más difícil de entender y mantener. Apoyo totalmente hacer refactorizaciones simples como esta en aras de la claridad.

(actualización) Acabo de intentar 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.

Leopd
fuente
3
Pude evitar el mensaje de error de Leopd al crear la migración del esquema usando --auto en lugar de --empty. Luego edité el archivo de migración, cambiando la eliminación / creación de las tablas en una llamada a db.rename_table (). Esto parece haber funcionado muy bien.
KFB
44
Utilicé esta técnica el 2/9/2011 sin obtener ningún error. Quizás una versión más nueva de South resolvió el problema con los errores.
Chip Tol
1
¡Gracias por mantener esto actualizado! La respuesta de Jian a continuación dice que es importante mantener las llamadas "send_create_signal", ¿tiene algún conocimiento al respecto? Si está de acuerdo, sería genial actualizar su ejemplo de migración.
mrooney
55
Tenga en cuenta que esto no cambiará el nombre de los índices en esa tabla. Si crea una nueva tabla en el futuro con el mismo nombre que la tabla anterior, puede obtener errores al colisionar los nombres de índice. Estábamos utilizando esta técnica, pero a partir de ahora vamos a crear explícitamente la nueva tabla, migrar los datos y luego eliminar la tabla anterior.
Jeremy Banks
3
Los nombres de columna en las tablas generadas automáticamente, como las tablas M2M al modelo original, tampoco se migran con este método.
spookylukey
66

Haz los cambios models.pyy luego ejecuta

./manage.py schemamigration --auto myapp

Cuando inspeccione 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 updatedeclaración, la db.send_create_signalllamada creará una nueva ContentTypecon el nuevo nombre del modelo. Pero es mejor solo updatelo ContentTypeque ya tiene en caso de que haya objetos de base de datos que lo señalen (por ejemplo, a través de a GenericForeignKey).

Además, si ha cambiado el nombre de algunas columnas que son claves foráneas para el modelo renombrado, no olvide

db.rename_column(myapp_model, foo_id, bar_id)
Jian
fuente
2
Recibo el error KeyError: "El modelo 'contenttype' de la aplicación 'contenttypes' no está disponible en esta migración". Además, tengo una tabla django_content_type, pero no una tabla contenttypes. (Django 1.6)
Seth
2
@Seth Trabajé alrededor de esto actualizando los modelos ContentType en una migración de datos separada y agregando el contenttypes.ContentTypemodelo a los modelos congelados usando el --frozenindicador a ./manage.py datamigration. Por ejemplo: ./manage.py datamigration --frozen contenttypes myapp update_contenttypes. Luego edite myapp_migrations / NNNN_update_contenttypes.py con el código de actualización del tipo de contenido como se especificó anteriormente.
Geoffrey Hing
@ GeoffreyHing Creo que el parámetro está congelado no congelado. south.readthedocs.io/en/latest/ormfreezing.html Pero muchas gracias por su ayuda, fue realmente útil.
ccsakuweb
5

South no puede hacerlo por sí mismo: ¿cómo sabe que Barrepresenta lo que Foosolía ser? Este es el tipo de cosas para las que escribiría una migración personalizada. Puede cambiar su ForeignKeycódigo de entrada como lo hizo anteriormente, y luego solo se trata de cambiar el nombre de los campos y tablas apropiados, lo que puede hacer de la forma 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 implementación, particularmente dada la disponibilidad de la verbose_nameopción Meta.

Dominic Rodger
fuente
77
Alternativamente, cambie el nombre del modelo en código pero use la db_tableopción Meta para mantener el mismo nombre de la tabla de la base de datos.
Daniel Roseman
@Daniel: ¿sabe si db_tablese usa para derivar nombres de claves externas?
Dominic Rodger
Creo que lo es. Si cambia el nombre del modelo y establece db_table, todo debería funcionar como se esperaba.
Davor Lucic
1
@DanielRoseman esta es la mejor solución en todo el hilo!
joerick
-1

Seguí la solución de Leopd anterior. 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 al sur, pero con la opción --fake. Esto hace que los nombres de modelo y de tabla sean iguales.

Acabo de darme cuenta, primero se puede comenzar cambiando los nombres de los modelos, luego editar el archivo de migraciones antes de aplicarlos. Mucho más limpio.

Gowthaman
fuente