Добавление таблицы "через" в поле django и миграция с помощью South?
Кажется, что это должно быть "легким" или, по крайней мере, документированным где-то, я просто не могу его найти.
Допустим, у меня есть модель:
class A(models.Model):
users = models.ManyToMany('auth.User', blank=True)
Теперь я хочу перейти на таблицу through
, чтобы добавить поля в отношение 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')
Тогда я делаю:
% ./manage.py schemamigration app --auto
Не совсем удивительно, он говорит мне, что он собирается отказаться от оригинальной автоматической сборки через таблицу и создать новую для AUsers
. Какая лучшая практика на данный момент? Есть ли достойный способ перехода на новую таблицу through
? Использовать ли db_table
в мета? Я просто не использую through=...
сразу... затем выполните schemamigration --auto
, затем datamigration
, чтобы скопировать текущую таблицу (как-то, не уверен...), а затем добавить отношение through
и позволить он убивает таблицу?
Какой трюк здесь? Это действительно так сложно?
Ответы
Ответ 1
Вы можете сделать это довольно легко.
Прежде всего, убедитесь, что руководство с помощью таблицы, которую вы создаете, имеет одно и то же имя таблицы в базе данных, которое было создано изначально созданного Django.
Итак, во-первых, рассмотрим руководство через модель до вашего изменения:
class AUsers(models.Model):
user = models.ForeignKey('auth.User')
a = models.ForeignKey('A')
class Meta:
db_table = 'appname_a_user'
Это должно быть функционально (почти) идентично ManyToManyField
, которое вы использовали. Фактически, вы могли выполнить пустую миграцию и применить ее, а затем использовать --auto для своих изменений (но не).
Теперь добавьте свое поле, как вы делали в приведенном выше примере кода, а затем запустите ./manage.py schemamigration appname manual_through_table --empty
. Это даст вам пустую миграцию с именем ####_manual_through_table.py
.
В самой миграции будет использоваться метод forwards
и backwards
. Каждая из них должна быть по одной строке:
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')
Это должно сделать то, что вам нужно.
Ответ 2
Если кто-то сталкивается с этим вопросом при попытке сделать то же самое с инфраструктурой переноса современных, вот шаги:
- Создайте новый класс модели, который точно соответствует встроенной таблице.
- Используйте класс Meta для установки имени таблицы в соответствии с существующей таблицей
- Сгенерируйте миграцию, которая создаст новую таблицу и установит ее как сквозную для поля.
- Не запуская эту миграцию, отредактируйте ее, чтобы обернуть ее в миграции
migrations. SeparateDatabaseAndState
, где автоматически сгенерированные шаги находятся в поле state_operations
, а операции с базой данных пусты.
- Измените свою таблицу, если необходимо, чтобы генерировать новые миграции как обычно.
Ответ 3
Как уже упоминалось в комментарии, первый шаг можно упростить, используя db.rename_table
, как описано здесь, который дает это через модель:
class AUsers(models.Model):
user = models.ForeignKey('auth.User')
a = models.ForeignKey('A')
class Meta:
unique_together = (('user', 'a'),)
Затем создайте миграцию с помощью --auto (таким образом, вы увидите имена таблиц DB) и замените содержимое на:
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')
Я просто применил его в своем проекте без проблем.