Могу ли я объединить миграцию схемы и данных (Юг) в одну?
Я хочу переместить поле 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