Перенос данных из "Много-ко-многим" в "Много-ко-многим через" в джанго

У меня есть модель

class Category(models.Model):
    title           = models.CharField(...)
    entry           = models.ManyToManyField(Entry,null=True,blank=True,
                                             related_name='category_entries',
                                             )

Я хочу реорганизовать дополнительные данные с каждой связью:

class Category(models.Model):
    title           = models.CharField(...)
    entry           = models.ManyToManyField(Entry,null=True,blank=True,
                                             related_name='category_entries',
                                             through='CategoryEntry',
                                             )

Но юг удаляет существующую таблицу. Как я могу сохранить существующие отношения m-t-m?

Ответы

Ответ 1

  • Создайте промежуточную модель без каких-либо дополнительных полей. Дайте ему уникальное ограничение для соответствия существующему и укажите имя таблицы в соответствии с существующим:

    class CategoryEntry(models.Model):
        category = models.ForeignKey(Category)
        entry = models.ForeignKey(Entry)   
    
        class Meta:
            db_table='main_category_entries'   #change main_ to your application
            unique_together = (('category', 'entry'))
    
  • Запустите миграцию схемы юга.

  • Отредактируйте сгенерированную миграцию схемы script и закомментируйте все записи вперед и назад, так как вы будете повторно использовать существующую таблицу пересечений. Добавьте pass, чтобы завершить методы.

  • Запустите миграцию.

  • Обновить любой существующий код. Как говорится в https://docs.djangoproject.com/en/dev/topics/db/models/#many-to-many-relationships, "В отличие от обычных полей" много-ко-многим "вы не можете использовать add, create или assign для создания отношений", поэтому вам нужно будет изменить любой существующий код приложения, например

    c.entry.add(e)
    

    может стать:

    try:
        categoryentry = c.categoryentry_set.get(entry = e)
    except CategoryEntry.DoesNotExist:
        categoryentry = CategoryEntry(category=c, entry=e)
        categoryentry.save()
    

    и

    e.category_entries.add(c)
    

    может стать:

    categoryentry = CategoryEntry(category=c, entry=e)  #set extra fields here
    categoryentry.save()                
    

    и

    c.entry.remove(e)
    

    может стать:

    categoryentry = c.categoryentry_set.get(entry = e)
    categoryentry.delete()
    
  • Как только эта начальная псевдо-миграция была выполнена, вы должны затем добавить дополнительные поля в CategoryEntry и создать дополнительные миграции как обычно.

Ответ 2

У меня была эта проблема в Django 1.7+, где миграции были частью ядра Django, после некоторых исследований мне удалось ее решить. В случае, если у кого-то есть проблемы, это решение:

Так как состояние "кода" базы данных обрабатывается по-разному на юге (полное состояние сохраняется при каждой миграции), а Django 1.7 (он рассчитывается из всех миграций), вам нужно указать code_state в Django, что модель имеет был добавлен, хотя он этого не сделал.

Как и выше, это нужно сделать в несколько шагов.

  • Создайте промежуточную модель, как в приведенном выше ответе:

    class CategoryEntry(models.Model):
        category = models.ForeignKey(Category)
        entry = models.ForeignKey(Entry)   
    
    class Meta:
        db_table='main_category_entries'   #change main_ to your application
        unique_together = (('category', 'entry'))
    
  • Создайте автоматизацию и измените код. Заменить список операций списком с помощью одного migrations.SeparateDatabaseAndState(database_operations, state_operations) с списком автогенерированных операций как аргумент state_operations и с пустым списком database_operations. Он будет выглядеть так:

    class Migration(migrations.Migration):
        operations = [
            migrations.SeparateDatabaseAndState(
               state_operations = [ <original operations list> ],
               database_operations= []
            ),
        ]
    
  • Отредактируйте CategoryEntry, чтобы содержать то, что вы хотите, и создайте новую автоматизацию.

Ответ 3

Я бы сделал это следующим образом:

  • Добавить класс CategoryEntry в модель и выполнить миграцию автоматической схемы. Это добавит пустую таблицу, содержащую свойства "CategoryEntry". Следует отметить, что старая таблица M2M остается нетронутой, так как "through =" CategoryEntry "еще не добавлено.

  • Проведите миграцию данных, чтобы скопировать все данные из существующей таблицы M2M в таблицу, созданную на шаге 1. Для этого запустите команду datamigration и измените методы forward() и backward() в автоматически сгенерированном миграция script соответственно.

  • Теперь добавьте часть = 'CategoryEntry' (именно так, как вы хотели) и выполните схему. это снизит старую таблицу M2M.

Надеюсь, это поможет. Удачи...