2015-06-19 13 views
10

Sto migrando il mio sito da un backend SQLite a un backend Postgres. Abbiamo eseguito migrazioni native in stile Django (ad es., Non in Sud) dall'inizio del progetto. La maggior parte delle migrazioni funziona bene, ma c'è un singhiozzo nelle nostre applicazioni.Migrazione di Django 1.8 impossibile trasmettere l'id della colonna al numero intero

Siamo arrivati ​​così lontano nella migrazione Postgres. Tutte le altre app sono state completamente migrate. Tutte le migrazioni sono state eseguite senza problemi su SQLite3.

processes 
[X] 0001_initial 
[X] 0002_auto_20150508_2149 
[ ] 0003_auto_20150511_1543 
[ ] 0004_auto_20150528_1739 
[ ] 0005_process_upstream 
[ ] 0006_auto_20150605_1436 
[ ] 0007_auto_20150605_1706 
[ ] 0008_milestone_prevailing_process 

Questi due migrazioni eseguite correttamente:

0001_initial.py:

class Migration(migrations.Migration): 

    dependencies = [ 
    ] 

    operations = [ 
     migrations.CreateModel(
      name='DateReason', 
      fields=[ 
       ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 
       ('name', models.CharField(unique=True, max_length=50)), 
       ('active', models.BooleanField(default=True)), 
       ('final', models.BooleanField(default=False)), 
      ], 
     ), 
     migrations.CreateModel(
      name='EventType', 
      fields=[ 
       ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 
       ('name', models.CharField(unique=True, max_length=50)), 
       ('active', models.BooleanField(default=True)), 
      ], 
     ), 
     migrations.CreateModel(
      name='Metric', 
      fields=[ 
       ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 
       ('name', models.CharField(unique=True, max_length=50)), 
       ('active', models.BooleanField(default=True)), 
      ], 
     ), 
     migrations.CreateModel(
      name='Process', 
      fields=[ 
       ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 
       ('name', models.CharField(max_length=50)), 
       ('sequence', models.PositiveIntegerField()), 
       ('sla', models.PositiveSmallIntegerField(null=True, blank=True)), 
       ('milestone', models.BooleanField(default=False)), 
      ], 
      options={ 
       'ordering': ['workflow', 'sequence'], 
      }, 
     ), 
     migrations.CreateModel(
      name='Workflow', 
      fields=[ 
       ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 
       ('name', models.CharField(unique=True, max_length=20)), 
       ('active', models.BooleanField(default=True)), 
      ], 
     ), 
     migrations.AddField(
      model_name='process', 
      name='workflow', 
      field=models.ForeignKey(to='processes.Workflow'), 
     ), 
     migrations.AlterUniqueTogether(
      name='process', 
      unique_together=set([('workflow', 'name'), ('workflow', 'sequence')]), 
     ), 
    ] 

0002_auto_20150508_2149.py:

class Migration(migrations.Migration): 

    dependencies = [ 
     ('processes', '0001_initial'), 
    ] 

    operations = [ 
     migrations.CreateModel(
      name='Milestone', 
      fields=[ 
       ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 
       ('name', models.CharField(max_length=50)), 
       ('sequence', models.PositiveIntegerField()), 
       ('workflow', models.ForeignKey(to='processes.Workflow')), 
      ], 
      options={ 
       'ordering': ['workflow', 'sequence'], 
      }, 
     ), 
     migrations.AlterModelOptions(
      name='process', 
      options={'ordering': ['milestone', 'sequence']}, 
     ), 
     migrations.AlterUniqueTogether(
      name='process', 
      unique_together=set([('milestone', 'name'), ('milestone', 'sequence')]), 
     ), 
     migrations.RemoveField(
      model_name='process', 
      name='workflow', 
     ), 
     migrations.AlterUniqueTogether(
      name='milestone', 
      unique_together=set([('workflow', 'name'), ('workflow', 'sequence')]), 
     ), 
    ] 

Questa migrazione non verrà eseguito: 0003_auto_20150511_1543.py

class Migration(migrations.Migration): 

    dependencies = [ 
     ('processes', '0002_auto_20150508_2149'), 
    ] 

    operations = [ 
     migrations.AlterModelOptions(
      name='process', 
      options={'ordering': ['milestone', 'sequence'], 'verbose_name_plural': 'processes'}, 
     ), 
     migrations.AlterField(
      model_name='process', 
      name='milestone', 
      field=models.ForeignKey(to='processes.Milestone'), 
     ), 
    ] 

Tentare di eseguire questo risultati della migrazione in:

django.db.utils.ProgrammingError: column "milestone_id" cannot be cast automatically to type integer 
HINT: Specify a USING expression to perform the conversion. 

L'attuale, lo stato completamente migrata delle pertinenti tabelle modello è:

class Milestone(models.Model): 
    """A collection of steps in a workflow""" 
    workflow = models.ForeignKey(Workflow, blank=False, null=False) 
    name = models.CharField(max_length=50, blank=False, null=False) 
    sequence = models.PositiveIntegerField(blank=False, null=False) 
    prevailing_process = models.ForeignKey('Process', blank=False, null=True, related_name='controls_milestone') 

    class Meta: 
     ordering = ['workflow', 'sequence'] 
     unique_together = (("workflow", "sequence"), ("workflow", "name")) 

    def __unicode__(self): 
     return u"{0}: {1}".format(self.workflow.name, self.name) 


class Process(models.Model): 
    """A step in a workflow""" 
    milestone = models.ForeignKey(Milestone, blank=False, null=False) 
    name = models.CharField(max_length=50, blank=False, null=False) 
    sequence = models.PositiveIntegerField(blank=False, null=False) 
    sla = models.PositiveSmallIntegerField(blank=True, null=True) 
    upstream = models.ForeignKey('self', blank=True, null=True, on_delete=models.SET_NULL, related_name='downstream_set') 

    class Meta: 
     ordering = ['milestone', 'sequence'] 
     unique_together = (("milestone", "sequence"), ("milestone", "name")) 
     verbose_name_plural = "processes" 

    def __unicode__(self): 
     return u"{0} {1}: {2}".format(self.milestone.workflow.name, self.milestone.name, self.name) 

Squashing le migrazioni non ha aiutato. Il database Postgres è pulito tranne per le definizioni di tabella. Le definizioni delle tabelle Postgres rilevanti nella loro forma bloccata sono:

scorecard=# \d processes_milestone 
            Table "public.processes_milestone" 
    Column |   Type   |       Modifiers 
-------------+-----------------------+------------------------------------------------------------------ 
id   | integer    | not null default nextval('processes_milestone_id_seq'::regclass) 
name  | character varying(50) | not null 
sequence | integer    | not null 
workflow_id | integer    | not null 
Indexes: 
    "processes_milestone_pkey" PRIMARY KEY, btree (id) 
    "processes_milestone_workflow_id_21e7e70ae59594a8_uniq" UNIQUE CONSTRAINT, btree (workflow_id, sequence) 
    "processes_milestone_workflow_id_363216929a08f11e_uniq" UNIQUE CONSTRAINT, btree (workflow_id, name) 
    "processes_milestone_846c77cf" btree (workflow_id) 
Check constraints: 
    "processes_milestone_sequence_check" CHECK (sequence >= 0) 
Foreign-key constraints: 
    "processes_workflow_id_53b7557aa3f3378e_fk_processes_workflow_id" FOREIGN KEY (workflow_id) REFERENCES processes_workflow(id) DEFERRABLE INITIALLY DEFERRED 

scorecard=# \d processes_process 
            Table "public.processes_process" 
    Column |   Type   |       Modifiers 
-----------+-----------------------+---------------------------------------------------------------- 
id  | integer    | not null default nextval('processes_process_id_seq'::regclass) 
name  | character varying(50) | not null 
sequence | integer    | not null 
sla  | smallint    | 
milestone | boolean    | not null 
Indexes: 
    "processes_process_pkey" PRIMARY KEY, btree (id) 
    "processes_process_milestone_20dc77c2825fcc38_uniq" UNIQUE CONSTRAINT, btree (milestone, name) 
    "processes_process_milestone_5bb869985140bf86_uniq" UNIQUE CONSTRAINT, btree (milestone, sequence) 
Check constraints: 
    "processes_process_sequence_check" CHECK (sequence >= 0) 
    "processes_process_sla_check" CHECK (sla >= 0) 
Referenced by: 
    TABLE "collection_implementation" CONSTRAINT "collection__process_id_6461d2ef37b3f126_fk_processes_process_id" FOREIGN KEY (process_id) REFERENCES processes_process(id) DEFERRABLE INITIALLY DEFERRED 

Sono praticamente senza idee. Sembra che sia già un numero intero e, in realtà, che altro si aspetterebbe che fosse una chiave primaria specificata da Django?

+0

FWIW, in alcuni casi, ho dovuto eliminare un campo prima, quindi aggiungere il diverso tipo di campo in una migrazione acquisite successivamente. – Brandon

+0

Non è una soluzione pulita, ma la migrazione colpevole sembra essere piuttosto piccola, forse è possibile modificare il database manualmente, quindi falsificare la migrazione? – spectras

+0

In seguito al commento di Brandon, l'ho risolto lasciando cadere il campo e aggiungendo di nuovo lo stesso campo in una successiva migrazione. – orange1

risposta

19

Il problema è la migrazione da Process.milestone come campo booleano a Process.milestone come chiave esterna. Postgres non aspetta che una migrazione fallisca con dati non cancellabili. Vuole una regola per modificare il tavolo in anticipo.

Se non si intende alcun tipo di migrazione dei dati tra due campi, l'opzione più semplice è semplicemente eliminare e aggiungere il campo. In questo caso specifico, ciò significherebbe cambiare le operazioni nel seguente modo:

operations = [ 
    migrations.RemoveField(
     model_name='process', 
     name='milestone' 
    ), 
    migrations.AddField(
     model_name='process', 
     name='milestone', 
     field=models.ForeignKey(to='processes.Milestone'), 
    ), 
    migrations.AlterModelOptions(
     name='process', 
     options={'ordering': ['milestone', 'sequence'], 'verbose_name_plural': 'processes'}, 
    ) 
] 
4

Postgres non sa come convertire un campo booleano in un numero intero, anche se la tabella è vuota. Devi dirlo con una clausola using. È possibile utilizzare una migrazione SQL per la conversione dei numeri interi prima di convertirli in una chiave esterna. Non penso che ci sia un modo per farlo senza SQL, Django non sa come farlo.

ALTER_SQL = ''' 
    ALTER TABLE processes_process ALTER COLUMN milestone TYPE integer USING (
     CASE milestone 
      when TRUE then ... 
      when FALSE then ... 
     END 
     ); 
    ''' 

class Migration(migrations.Migration): 

    dependencies = [ 
     ('processes', '0002_auto_20150508_2149'), 
    ] 

    operations = [ 
     migrations.AlterModelOptions(
      name='process', 
      options={'ordering': ['milestone', 'sequence'], 'verbose_name_plural': 'processes'}, 
     ), 
     migrations.RunSQL(ALTER_SQL, None, [ 
      migrations.AlterField(
       model_name='process', 
       name='milestone', 
       field=models.IntegerField(), 
       preserve_default=True, 
      ), 
     ]), 
     migrations.AlterField(
      model_name='process', 
      name='milestone', 
      field=models.ForeignKey(to='processes.Milestone'), 
     ), 
    ] 

Se la tabella è vuota, si potrebbe essere in grado di cavarsela con un utilizzo clausola vuota o un bossolo vuoto.

+0

L'aggiunta della clausola USING a un'operazione RunSQL non ha funzionato (stava ancora generando lo stesso errore), ma cercando di capire dove è andato storto almeno mi ha portato a una parte migliore dei documenti di Django per risolvere il problema me stessa. Lo apprezzo un sacco! –

+0

Qual è l'errore? E qual'era la tua clausola USING? – noamk

+0

L'errore era esattamente lo stesso. Ho provato sia una clausola USING vuota che l'impostazione a null per true e false. –

Problemi correlati