Могу ли я объединить миграцию схемы и данных (Юг) в одну?

Я хочу переместить поле honk, а его данные с одной модели на другую с помощью юга:

class Foo(models.Model):
    foofield = models.CharField()
    honk = models.PositiveIntegerField()

class Bar(models.Model):
    barfield = models.CharField()

Я сделал это раньше, используя 3 отдельные миграции:

  • Миграция схемы, добавив honk в Бар
  • Перенос данных, копирование всех Foo.honk данных в Bar.honk
  • Другая миграция схемы, отбрасывающая хонк от Foo

Могу ли я выполнить эти три шага в одном переносе?

Я уже узнал, что не так много различий между схемами и миграциями данных на юге, поэтому я подумал, что возможно что-то вроде этого может работать (что это три миграции выше, просто перепутаны в один):

class Migration(DataMigration):
    def forwards(self, orm):
        # add column
        db.add_column('myapp_bar', 'honk', self.gf('django.db.models.fields.PositiveIntegerField')(default='0'), keep_default=False)

        # copy data
        for foo in Foo.objects.all():
            # find the right bar here and then ...
            bar.honk = foo.honk
            bar.save()

        # remove old column
        db.delete_column('myapp_foo', 'honk')

Будет ли это работать или не получится, потому что мой (южный замороженный) orm еще не знает о Bar.honk? Или я делаю это неправильно, и есть ли лучший способ сделать это в одном переносе?

Ответы

Ответ 1

Поскольку этот вопрос заработал мне значок Tumbleweed, я сам выкопал и попробовал. Вот что я узнал.

Нет, вы не можете объединить эти миграции

Потому что замерзание ORM содержит только схему, к которой вы переходите. Таким образом, в приведенном выше примере foo.honk не будет доступен во время переноса данных (цикл для), поскольку он удаляется во время миграции схемы, поэтому он не является в замороженном ОРМ. Кроме того, вы получите исключения DatabaseError, если попытаетесь получить доступ к данным, потому что столбцы в базе данных еще не соответствуют тем из них (т.е. Если вы пытаетесь получить доступ к чему-либо до db.add_column),.

Похоже, что нет простого ярлыка и что-то вроде этого происходит с 3 миграциями, упомянутыми выше.

Ответ 2

Документация отсутствует с этой точки зрения, но если вы измените замороженную часть ORM в процессе миграции, добавив себе недостающее поле, тогда она будет доступна: я имею в виду, что во время южного перехода вы должны использовать замороженный ORM, так как в будущем вы будете мигрировать, модель Foo могла потерять поле honk.

Я думаю, что если вы измените объявление замороженного ORM, как показано ниже

models = {
    'app.foo': {
        'Meta': {'object_name': 'Foo'},
        'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
        'foofield': ('django.db.models.fields.CharField', [],{'max_length':666}),
        'honk': ('django.db.models.fields.PositiveIntegerField', [], {}),
    },
    'app.bar': {
        'Meta': {'object_name': 'Bar'},
        'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
        'barfield': ('django.db.models.fields.CharField', [],{'max_length':666}),
        'honk': ('django.db.models.fields.PositiveIntegerField', [], {}),
    },
}

complete_apps = ['app']
symmetrical = True

все будет работать:)

Трюк - это определение поля honk в каждой модели, очевидно, что столбец в базе данных должен присутствовать

class Migration(DataMigration):
    def forwards(self, orm):
        # add column
        db.add_column('myapp_bar', 'honk', self.gf('django.db.models.fields.PositiveIntegerField')(default='0'), keep_default=False)

        # copy data
        for foo in Foo.objects.all():
            # find the right bar here
            bar = orm.Bar.objects.get(**whatever)
            bar.honk = foo.honk
            bar.save()

        # remove old column
        db.delete_column('myapp_foo', 'honk')

PS:, как указано в @acjohnson55 symmetrical = True, действительно важно

Ответ 3

это работает для меня:

def migratedata(orm):
   # copy data, need to lookup model through orm.
   for foo in orm['myapp.foo'].objects.all():
      # find the right bar here and then ...
      bar.honk = foo.honk
      bar.save()

class Migration(SchemaMigration):
   def forwards(self, orm):
      # add column
      db.add_column('myapp_bar', 'honk', self.gf('django.db.models.fields.PositiveIntegerField')(default='0'), keep_default=False)
      # migrate data
      if not db.dry_run:
         migratedata(orm)
      # remove old column
      db.delete_column('myapp_foo', 'honk')

Однако я не рекомендую это, потому что это легко испортить. Особую осторожность необходимо учитывать для уникальности и порядка операций (IOW, не переносить данные после того, как вы удалили соответствующие поля (:)

Ответ 4

Как отметил Ингмар, южный ORM застывает в определенный момент времени, что препятствует доступу к столбцам, о которых ORM не знает. Однако на самом деле есть способ обойти это: вам не нужно использовать ORM или даже любой ORM; вместо этого вы можете выполнить необработанные SQL-запросы

Так, например, вместо

for foo in Foo.objects.all():
    print foo.honk

вы можете сделать что-то вроде:

cursor.execute('SELECT "honk" FROM "myapp_foo"')
for honk, in cursor.fetchall():
    print honk