2011-09-07 36 views
16

Estoy usando South con mi aplicación Django. Tengo dos modelos que estoy cambiando de tener una relación ForeignKey a tener una relación OneToOneField. Cuando ejecuté esta migración en mi base de datos de desarrollo, funcionó bien. Cuando las migraciones se ejecutan como parte de la creación de una base de datos de prueba, la última migración falla con un error MySQL 1005: "No se puede crear la tabla mydb. # Sql-3249_1d (errno: 121)". Al hacer algunas búsquedas en Google, se reveló que esto suele ser un problema al tratar de agregar una restricción con el mismo nombre que una restricción existente. La línea específica en la migración que se produce un error en es:Django - Cambiar una relación ForeignKey con OneToOne

La relación fue cambiado de:

class MyModel(models.Model): 
    othermodel = models.ForeignKey(OtherModel) 

a

class MyModel(models.Model): 
    othermodel = models.OneToOneField(OtherModel) 

que generó las siguientes declaraciones en la migración:

db.alter_column('myapp_mymodel', 'othermodel_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['myapp.OtherModel'], unique=True)) 

db.create_unique('myapp_mymodel', ['othermodel_id']) 

Pero en lugar de fallar en la llamada create_unique, está fallando en el alter_column llamada. Me encontré con el siguiente comando para ver qué SQL se está generando:

python manage.py migrate myapp 0010 --db-dry-run --verbosity=2 

y se imprimieron

myapp:0010_auto__chg_field_mymodel_othermodel__add_unique_mymodel 
    = ALTER TABLE `myapp_mymodel` ADD CONSTRAINT `myapp_mymodel_othermodel_id_uniq` UNIQUE (`othermodel_id`) [] 
    = SET FOREIGN_KEY_CHECKS=1; [] 
    = ALTER TABLE `myapp_mymodel` ADD CONSTRAINT `myapp_mymodel_othermodel_id_uniq` UNIQUE (`othermodel_id`) [] 

Parece extraño que se trata de abrir el ADD CONSTRAINT dos veces, pero si quito el db.create_unique llamada, no se genera SQL cuando lo ejecuto con --db-dry-run, pero aún obtengo el error si lo ejecuto de manera real.

Estoy perdido, cualquier ayuda es apreciada.

+1

He creado exactamente la misma migración hace unos días y que funcionó muy bien. ¿Podría probar el mismo código con un backend de base de datos diferente (lo hice en una base de datos PostgreSQL). Además, verifique su versión Sur. – emyller

+0

Ojalá pudiera ayudar, hice el cambio, generó el mismo código python y SQL, y la migración funcionó perfectamente, usando mysql 5.1.56 para win32. – Hannele

+0

Pregunta esto en la lista de correo del Sur y es muy probable que encuentres la respuesta. –

Respuesta

0

Lo primero que intentaría sería agregar un db.delete_unique(...) en varios lugares para ver si podía hackearlo.

De no ser así, me gustaría dividirlo en 3 migraciones:

  1. una migración de esquema para añadir una nueva columna para el OneToOne
  2. una migración de datos a copiar todos los valores de la columna de edad FK a nuevo
  3. una migración de esquema para eliminar la columna anterior, que luego editaba manualmente para incluir un comando para cambiar el nombre de la columna nueva a la anterior.
11

En realidad, no necesita una migración. Las relaciones OneToOne y ForeignKey tienen un esquema de base de datos compatible bajo el gancho: una columna simple con el otro ID de objeto en una de las tablas.

Simplemente falsifique la migración con migrate --fake si no quiere entrar en la molestia de decirle al sur que ignore este cambio.

+0

Y si tiene dudas de que @e-satis es correcto acerca de esto, tome este tidbit del código ('django.db.models.fields.related'):" OneToOneField es esencialmente lo mismo que ForeignKey , con la excepción de que siempre lleva consigo una restricción 'única'. Y tenga en cuenta que OneToOne realmente hereda de ForeignKey con solo algunos pequeños ajustes. – mlissner

+0

Para ser precisos, cambiar un campo de ForeignKey a OneToOneField TIENE un efecto en la base de datos (al menos en esas restricciones de soporte), que no se debe omitir: una ForeignKey no es única, mientras que OneToOneField sí lo es. En Django 1.8 - usando el comando nativo 'makemigrations' en el backend de mysql - no tuve ningún problema en absoluto. La migración borró correctamente el índice previo no único en el campo en el nivel de base de datos y estableció el nuevo índice único. – baxeico

1

Estoy de acuerdo con @e-satis que el objetivo aquí es falsificar una migración, pero sugiero un enfoque diferente si está trabajando con un equipo.

Si crea una migración y luego --fake, todos los miembros de su equipo necesitarán recordarlo también al --fake. Si alguno de ellos no hace esto cuando se actualizan, estás en problemas.

Un mejor enfoque es crear una migración vacío, entonces migrarla:

manage.py schemamigration yourapp --empty fake_migration_of_foreign_key_to_onetoone 
manage.py migrate # Like you always do! 
Cuestiones relacionadas