Django - ¿Cómo cambiar el nombre de un campo modelo usando South?

209

Me gustaría cambiar un nombre de campos específicos en un modelo:

class Foo(models.Model):
    name = models.CharField()
    rel  = models.ForeignKey(Bar)

debería cambiar a:

class Foo(models.Model):
    full_name     = models.CharField()
    odd_relation  = models.ForeignKey(Bar)

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

Jonathan
fuente
77
Consulte también stackoverflow.com/questions/2862979/… para cambiar el nombre de un modelo en lugar de un campo de modelo .
Caracol mecánico

Respuestas:

230

Puedes usar la db.rename_columnfunción.

class Migration:

    def forwards(self, orm):
        # Rename 'name' field to 'full_name'
        db.rename_column('app_foo', 'name', 'full_name')




    def backwards(self, orm):
        # Rename 'full_name' field to 'name'
        db.rename_column('app_foo', 'full_name', 'name')

El primer argumento de db.rename_columnes el nombre de la tabla, por lo que es importante recordar cómo Django crea los nombres de las tablas :

Django deriva automáticamente el nombre de la tabla de la base de datos del nombre de su clase de modelo y la aplicación que la contiene. El nombre de la tabla de la base de datos de un modelo se construye uniendo la "etiqueta de la aplicación" del modelo, el nombre que usó en manage.py startapp, con el nombre de clase del modelo, con un guión bajo entre ellos.

En el caso de que tenga un nombre de modelo con múltiples camellos, como ProjectItem, el nombre de la tabla será app_projectitem(es decir, no se insertará un guión bajo projecty itemaunque estén en camello).

googletorp
fuente
2
Eso funcionaría en name \ full_name, pero no en el campo de relación, ¿verdad?
Jonathan
23
NOTA IMPORTANTE: si va a usar esto, asegúrese de que "app_foo" sea el nombre de la tabla de la base de datos, por ejemplo: "mainapp_profile", "name" es el nombre de la columna anterior de la base de datos (no el nombre del campo del modelo), por ejemplo: "user_id" y "full_name" serían el nuevo nombre que desea que tenga la columna (nuevamente, la columna de la base de datos y no el nombre del campo). Entonces: db.rename_column ('mainapp_profile', 'user_id', 'new_user_id') Además, si se trata de claves foráneas, debe tener la parte "_id" al cambiar el nombre.
Gezim
3
Si hace esto manualmente db.rename_column, puede que tenga que hacer un esquema falso de migración después para limpiar las cosas nuevamente. Eso es primero migrar el cambio con cambiar el nombre de las columnas. Luego arregle el modelo (para tener el nombre actualizado) y luego haga la aplicación ./manage.py schemamigration --auto && ./manage.py migrate app --fake).
dr jimbob
24
También puede usar ./manage.py schemamigration my_app renaming_column_x --empty para crear una migración vacía y solo para colocar el código en ella
Ilian Iliev
11
--empty no ayuda mucho, en su lugar usa --auto y modifica la migración creada. De esta forma, no reclamará que el campo se haya eliminado para la próxima migración de models.py.
yasc
39

Esto es lo que hago:

  1. Haga que el nombre de la columna cambie en su modelo (en este ejemplo sería myapp/models.py)
  2. correr ./manage.py schemamigration myapp renaming_column_x --auto

Note renaming_column_xpuede ser lo que quiera, es solo una forma de dar un nombre descriptivo al archivo de migración.

Esto generará un archivo llamado myapp/migrations/000x_renaming_column_x.pyque eliminará su columna anterior y agregará una nueva columna.

Modifique el código en este archivo para cambiar el comportamiento de migración a un simple cambio de nombre:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming column 'mymodel.old_column_name' to 'mymodel.new_column_name'
        db.rename_column(u'myapp_mymodel', 'old_column_name', 'new_column_name')

    def backwards(self, orm):
        # Renaming column 'mymodel.new_column_name' to 'mymodel.old_column_name'
        db.rename_column(u'myapp_mymodel', 'new_column_name', 'old_column_name')
donturner
fuente
¿Cuál es el nombre de la columna en su comando? xo column_x?
andilabs
La parte del comando al que te refieres solo se usa para nombrar la migración, puede ser lo que quieras. El nombre de la columna deberá especificarse en el archivo de migración cuando lo edite.
donturner
2
Crear la --automigración primero es un gran consejo. Evita problemas con el congelador South ORM, que ocurre si la migración solo tiene métodos forwardsy backwardsmétodos, pero no contiene el modelobjeto congelado .
mwcz
¿Cómo respondo la pregunta de south 'Ya que está eliminando este campo, DEBE especificar un valor predeterminado'?
Bryce
3
El único problema que tengo con este método es que db.rename_columnno cambia el nombre de las restricciones asociadas con la columna. La migración seguirá funcionando, pero tendrá restricciones con el nombre de la columna anterior. Tenía una columna con una restricción de unicidad, la renombré usando este método, probé que la restricción de unicidad aún existía y obtuve un error, pero el nombre de la restricción todavía estaba usando el nombre de la columna anterior. Quizás explícito db.delete_uniquey db.create_uniquelo hubiera hecho, pero decidí ir con la solución de sjh.
Louis
15

No sabía acerca de la columna db.rename, parece útil, sin embargo, en el pasado agregué la nueva columna como un esquema de migración, luego creé una migración de datos para mover los valores al nuevo campo, luego una segunda migración de esquema para eliminar la columna anterior

sjh
fuente
Yo también. El problema con la técnica de esquema de datos de esquema es que terminas con diferentes nombres para tus campos / modelos. A veces esto es un problema si usas nombres canónicos para habilitar a los despachadores ...
Jonathan
Acabo de intentar esto y no funcionó ya que hubo un conflicto de nombres. Tenía exactamente el mismo FK pero diferente nombre. No validado. Entonces, no hagas esto.
Gezim
2
@ Jonathan, ¡quiere nombres diferentes! ... @pilgrim, ¿quieres publicar algún código? Lo he hecho varias veces esta semana, si sus modelos no se validan, entonces el sur no creará las migraciones.
sjh
Eso funcionaría, pero probablemente sería más lento que la 'rename_column' que otras personas han sugerido. Sé que MySQL puede hacer una "ALTERAR TABLA ... cambiar el nombre de la columna" (o lo que sea) con bastante rapidez.
Rory
1
@Rory db.rename_columnno cambiará el nombre de las restricciones para usted, por lo que debe manejarlo manualmente. Si olvida hacerlo, la migración funcionará, excepto que, sin que lo sepa, puede haber una restricción que todavía use el nombre de la columna anterior. No me queda claro si el problema es meramente cosmético o si en una futura migración donde la restricción debe ser manipulada o eliminada, el Sur no podrá encontrarla. En cualquier caso, hacerlo aquí como sugiere sjh es la forma segura de hacerlo: puedes dejar que South descubra lo que debería estar resolviendo.
Louis
10

Django 1.7 introdujo Migraciones, por lo que ahora ni siquiera necesita instalar un paquete adicional para administrar sus migraciones.

Para cambiar el nombre de su modelo, primero debe crear una migración vacía:

$ manage.py makemigrations <app_name> --empty

Luego debe editar el código de su migración de esta manera:

from django.db import models, migrations

class Migration(migrations.Migration):

dependencies = [
    ('yourapp', 'XXXX_your_previous_migration'),
]

operations = [
    migrations.RenameField(
        model_name='Foo',
        old_name='name',
        new_name='full_name'
    ),
    migrations.RenameField(
        model_name='Foo',
        old_name='rel',
        new_name='odd_relation'
    ),
]

Y después de eso necesitas correr:

$ manage.py migrate <app_name>
Dmitrii Mikhailov
fuente
5

Simplemente cambie el modelo y ejecute makemigrationsen 1.9

Django detecta automáticamente que ha eliminado y creado un solo campo, y le pregunta:

Did you rename model.old to model.new (a IntegerField)? [y/N]

Diga sí y se creará la migración correcta. Magia.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
0
  1. Agregue southa sus aplicaciones instaladas en el archivo de configuración del proyecto.
  2. Comente el campo / tabla agregado / modificado.
  3. $ manage.py Schemamigration <app_name> --initial
  4. $ manage.py migrate <app_name> --Fake
  5. Descomente el campo y escriba el modificado
  6. $ manage.py Schemamigration --auto
  7. $ manage.py migrate <app_name>

Si está usando 'pycharm', puede usar 'ctrl + shift + r' en lugar de 'manage.py' y 'shift' para los parámetros.

ancho
fuente