2011-05-19 10 views
13

Parece que esto debería ser "fácil" o al menos documentado en alguna parte, simplemente no puedo encontrarlo.¿Agregar una tabla "directa" al campo django y migrar con el sur?

Digamos que tengo un modelo:

class A(models.Model): 
    users = models.ManyToMany('auth.User', blank=True) 

Ahora quiero migrar a tener una mesa through añadir campos a la relación ManyToMany ...

class AUsers(models.Model): 
    user = models.ForeignKey('auth.User') 
    a = models.ForeignKey('A') 
    new_field = models.BooleanField() 

class A(models.Model): 
    users = models.ManyToMany('auth.User', blank=True, through='AUsers') 

entonces yo:

% ./manage.py schemamigration app --auto 

No es del todo sorprendente, me dice que va a dejar caer el auto creado originalmente a través de la tabla y crear uno nuevo para AUsers. ¿Cuál es la mejor práctica en este punto? ¿Hay una forma decente de migrar a la nueva tabla through? ¿Uso db_table en Meta? ¿Simplemente no uso el through=... de inmediato ... luego hago un schemamigration --auto, luego un datamigration para copiar la tabla actual (de alguna manera, no estoy seguro ...) y luego agrego la relación through y dejo que mate la tabla?

¿Cuál es el truco aquí? ¿Es esto realmente tan difícil?

Respuesta

14

Debería poder hacer esto con bastante facilidad.

En primer lugar, asegúrese de que la tabla manual que está creando tiene el mismo nombre de tabla en la base de datos que el creado originalmente por Django.

Así, en primer lugar, vamos a considerar un manual a través del modelo antes de su cambio:

class AUsers(models.Model): 
    user = models.ForeignKey('auth.User') 
    a = models.ForeignKey('A') 

    class Meta: 
     db_table = 'appname_a_user' 

Eso debería ser funcionalmente (casi) idéntica a la ManyToManyField que solía tener. En realidad, usted podría hacer una migración vacía y aplicarla, y luego usar --auto para sus cambios (pero no).

Ahora, agregue su campo como lo hizo en su código de ejemplo anterior, y luego ejecute ./manage.py schemamigration appname manual_through_table --empty. Eso le dará una migración vacía llamada ####_manual_through_table.py.

En la migración en sí, habrá un método forwards y backwards. Cada uno necesita ser una línea cada uno:

def forwards(self, orm): 
    db.add_column('appname_a_user', 'new_field', self.gf('django.db.models.fields.BooleanField')(default=False)) 

def backwards(self, orm): 
    db.delete_column('appname_a_user', 'new_field') 

Eso debería conseguir lo que busca.

+0

Sí, me preguntaba si era una migración manual obligatoria. Parece que ya que piensas lo mismo ... eso es correcto. El db_table es también algo que supongo que tendré que hacer también. Simplemente parece una pieza de "cruft" que solo existe debido a su historial de migración y no es necesaria ... pero supongo que eso es lo que haré. Voy a marcar aceptar si/cuando funciona. – dlamotte

+1

También debe agregar un unique_constraint para que la migración sea "completa". Siento que esto fue un poco demasiado complicado ... ¿al menos más de lo necesario? Dejaré esto abierto durante unos días y veré si alguien tiene una solución mejor ... Me gustaría ver una ... ¿Tal vez tenga que hackear a South para agregarla en el futuro? – dlamotte

+0

@dlamotte acaba de encontrarse con el mismo escenario yo mismo. Puedes (ahora, no sé de entonces) usar 'db.rename_table' para cambiar el nombre de la tabla a lo que quieras. – antitoxic

0

Como se mencionó en un comentario, el primer paso se puede simplificar usando db.rename_table como se describe here, lo que le da a este modelo a través de:

class AUsers(models.Model): 
user = models.ForeignKey('auth.User') 
a = models.ForeignKey('A') 

class Meta: 
    unique_together = (('user', 'a'),) 

A continuación, crear una migración con --auto (esta manera usted tener los nombres de las tablas de DB visibles), y reemplazar el contenido con:

class Migration(SchemaMigration): 

    def forwards(self, orm): 
     db.rename_table('appname_a_user', 'appname_auser') 

    def backwards(self, orm): 
     db.rename_table('appname_auser','appname_a_user') 

Acabo de aplicarlo en mi proyecto sin problemas.

3

Si alguien viene a través de esta pregunta cuando se trata de hacer lo mismo con el marco de la migración modernos, aquí están los pasos:

  1. crear una nueva clase de modelo que coincide exactamente con el built-in a través de la mesa
  2. Utilice la clase Meta para establecer el nombre de la tabla para que coincida con la tabla existente
  3. Genere una migración, que creará la nueva tabla y la establecerá como el final para el campo.
  4. Sin ejecutar esa migración, edítela para envolverla en una migración migrations. SeparateDatabaseAndState, donde los pasos generados automáticamente están en el campo state_operations y las operaciones de la base de datos están vacías.
  5. Modifique su tabla directa, según sea necesario, asegurándose de generar nuevas migraciones de forma normal.
Cuestiones relacionadas