2015-06-19 9 views
10

Ich migriere meine Website von einem SQLite-Back-End in ein Postgres-Back-End. Wir haben seit Beginn des Projekts Migrationen im systemeigenen Django-Stil durchgeführt (d. H. Nicht im Süden). Die meisten Migrationen laufen gut, aber es gibt einen Schluckauf in unseren Anwendungen.Migration von Django 1.8 kann Spalten-ID nicht in Ganzzahl umwandeln

Wir sind so weit in der Postgres Migration. (Alle anderen Apps wurden vollständig migriert.) Alle Migrationen wurden ohne Vorfälle auf SQLite3 ausgeführt.

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 

Diese beiden Migrationen lief richtig:

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')]), 
     ), 
    ] 

Diese Migration wird nicht ausgeführt: 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'), 
     ), 
    ] 

Versuch, diese Migration führt zu laufen:

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

Der aktuelle, voll migrierten Zustand der relevanten Modelltabellen ist:

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) 

die Migrationen Quetschen nicht helfen. Die Postgres-Datenbank ist bis auf die Tabellendefinitionen sauber. Die relevanten Postgres Tabellendefinitionen in ihrem steckengebliebenen Formular sind:

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 

Ich bin im Grunde aus Ideen. Es sieht so aus, als ob es bereits eine ganze Zahl ist, und was würde eigentlich ein Django-spezifizierter Primärschlüssel erwarten?

+0

fwiw, In einigen Fällen musste ich zuerst ein Feld löschen und dann die andere Art von Feld in eine nachfolgende Migration einfügen. – Brandon

+0

Das ist keine saubere Lösung, aber die schuldige Migration scheint ziemlich klein zu sein. Vielleicht können Sie die Datenbank manuell ändern und dann die Migration fälschen? – spectras

+0

Brandon's Kommentar wurde hinzugefügt, indem ich das Feld lösche und das gleiche Feld bei einer nachfolgenden Migration erneut hinzufüge. – orange1

Antwort

19

Das Problem ist die Migration von Process.milestone als boolesches Feld zu Process.milestone als Fremdschlüssel. Postgres wartet nicht auf eine fehlgeschlagene Migration auf nicht übertragbaren Daten. Es möchte, dass eine Regel die Tabelle im Voraus ändert.

Wenn Sie keine Datenmigration zwischen zwei Feldern beabsichtigen, ist es am einfachsten, das Feld einfach zu löschen und hinzuzufügen. In diesem speziellen Fall würde das bedeuten, die Operationen wie folgt zu ändern:

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 weiß nicht, wie ein boolesches Feld in eine Ganzzahl konvertiert wird, selbst wenn die Tabelle leer ist. Sie müssen es mit einer using-Klausel sagen. Sie können eine SQL-Migration für die Integer-Konvertierung verwenden, bevor Sie in einen Fremdschlüssel konvertieren. Ich denke nicht, dass es einen Weg gibt, dies ohne SQL zu tun, django weiß nicht, wie man das macht.

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'), 
     ), 
    ] 

Wenn die Tabelle leer ist, können Sie möglicherweise mit einer leeren using-Klausel oder einem leeren Fall davonkommen.

+0

Das Hinzufügen der USING-Klausel zu einer RunSQL-Operation hat nicht funktioniert (es war immer noch der gleiche Fehler), aber zu versuchen, herauszufinden, wo es schief gelaufen ist, brachte mich zumindest zu einem besseren Teil der Django-Dokumentation mich selber. Ich weiß es zu schätzen! –

+0

Was war der Fehler? Und was war deine USING-Klausel? – noamk

+0

Der Fehler war genau der gleiche. Ich probierte sowohl eine leere USING-Klausel aus und setzte sie für "True" und "False" auf null. –

Verwandte Themen