在django中将数据从"多对多"迁移到"多对多通"

Bry*_*yce 17 django django-models django-south

我有一个模特

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

我希望重构每个关系的附加数据:

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

但是南方删除了现有的表格.我如何保留现有的mtm关系?

ygr*_*ram 27

在Django 1.7+内置迁移中,计算"代码状态"(即模型的代码定义)的方式是不同的,并且需要不同的解决方案.

在South(Django pre-1.7)中,整个"代码状态"保存在每次迁移中 - 但是在Django 1.7+内置迁移中,它是从查看整个迁移集中得出的,因此您需要指定"代码"陈述"在不改变数据库的情况下改变迁移.

如上所述,这需要在几个步骤中完成.

  1. 创建一个中间模型,如上面的答案:

    class CategoryEntry(models.Model):
        category = models.ForeignKey(Category, on_delete=models.CASCADE)
        entry = models.ForeignKey(Entry, on_delete=models.CASCADE)   
    
        class Meta:
             db_table = 'main_category_entries'   #change main_ to your application
             unique_together = ('category', 'entry')
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用django-admin.py makemigrations和修改代码创建自动迁移; 将操作列表移动到操作的state_operations参数中migrations.SeparateDatabaseAndState,并将database_operations列表保留为空.它应该看起来像:

    class Migration(migrations.Migration):
        operations = [
            migrations.SeparateDatabaseAndState(
                state_operations=[ 
                    migrations.CreateModel(CategoryEntry..)
                    ...
                ],
                database_operations=[]
            ),
        ]
    
    Run Code Online (Sandbox Code Playgroud)
  3. 编辑CategoryEntry以包含您想要的内容并使用创建新的自动迁移django-admin.py makemigrations

  • django docs关于SeparateDatabaseAndState的链接:https://docs.djangoproject.com/en/1.7/ref/migration-operations/#separatedatabaseandstate (3认同)

gre*_*reg 21

  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'))
    
    Run Code Online (Sandbox Code Playgroud)
  2. 运行南模式迁移.

  3. 编辑生成的模式迁移脚本并注释掉所有前向和后向条目,因为您将重新使用现有的交集表.添加pass完成方法.

  4. 运行迁移.

  5. 更新任何现有代码.正如在https://docs.djangoproject.com/en/dev/topics/db/models/#many-to-many-relationships中所说,"与普通的多对多字段不同,你不能使用add,创建或分配以创建关系"因此您需要修改任何现有的应用程序代码,例如

    c.entry.add(e)
    
    Run Code Online (Sandbox Code Playgroud)

    可能成为:

    try:
        categoryentry = c.categoryentry_set.get(entry = e)
    except CategoryEntry.DoesNotExist:
        categoryentry = CategoryEntry(category=c, entry=e)
        categoryentry.save()
    
    Run Code Online (Sandbox Code Playgroud)

    和:

    e.category_entries.add(c)
    
    Run Code Online (Sandbox Code Playgroud)

    可能成为:

    categoryentry = CategoryEntry(category=c, entry=e)  #set extra fields here
    categoryentry.save()                
    
    Run Code Online (Sandbox Code Playgroud)

    和:

    c.entry.remove(e)
    
    Run Code Online (Sandbox Code Playgroud)

    可能成为:

    categoryentry = c.categoryentry_set.get(entry = e)
    categoryentry.delete()
    
    Run Code Online (Sandbox Code Playgroud)
  6. 完成此初始伪迁移后,您应该能够将额外字段添加到其中CategoryEntry并正常创建进一步的迁移.


Zuf*_*fra 5

Django 文档将这种确切的情况作为migrations.SeparateDatabaseAndState 操作的应用程序。我完全按照文档所述进行操作,但 Django 不断抛出异常,指出用于 M2M 映射的表不存在。

我通过中间模型的 Meta 类的“db_table”属性分配一个表名,这导致了问题(不知道为什么)。然后我明白了,Django文档中的示例中显示的SQL代码是将Django为标准M2M关系中的M2M关系表分配的名称更改为Django为所使用的中介模型对应的表分配的新名称。

            database_operations=[
            # Old table name from checking with sqlmigrate, new table
            # name from AuthorBook._meta.db_table.
            migrations.RunSQL(
                sql='ALTER TABLE core_book_authors RENAME TO core_authorbook',
                reverse_sql='ALTER TABLE core_authorbook RENAME TO core_book_authors',
            ),
        ],
Run Code Online (Sandbox Code Playgroud)

在此示例中,“core_book_authors”是M2M关系表的旧名称,“core_authorbook”是M2M关系表的新名称。如果您不在迁移中包含此代码,您将无法向中间模型添加额外的字段(我认为这是拥有自定义 M2M 关系的主要原因),因为 Django 将查找新的表名称。

总结一下我使用“through”将标准 M2M 关系更改为自定义关系所做的工作:

  1. 创建了没有额外字段(只有两个外键)的中间模型,并指定现在通过该模型建立 M2M 关系(使用“通过”)。
  2. 运行命令py manage.py makemigrations。我将此自动生成的迁移更改为类似于上面引用的文档中的迁移。
  3. 运行命令py manage.py migrate
  4. 在中间模型中添加了我需要的所有额外字段。
  5. 运行命令py manage.py makemigrations
  6. 运行命令py manage.py migrate

之前表示标准 M2M 关系的表现在将具有不同的名称和所有新列。如果表中已有数据,那么这些列具有默认值非常重要。我发现这是最直接的方法,不会丢失任何数据。