更改模型以添加"通过"关系以订购ManytoMany字段 - Django 1.7迁移修改

Alb*_*lla 10 migration django manytomanyfield django-1.7 django-migrations

我正在尝试向我之前创建的ManyToMany字段添加订单.我基本上想订购图片集中的图片.我在Django 1.7上运行,所以没有更多的南迁移(我试图遵循这个教程:http://mounirmesselmeni.github.io/2013/07/28/migrate-django-manytomany-field-to-manytomany-through -with -南/)

这是我的"通过"关系:

class CollectionPictures(models.Model):
    picture = models.ForeignKey(
        Picture,
        verbose_name=u'Picture',
        help_text=u'Picture is included in this collection.',
    )
    collection = models.ForeignKey(
        Collection,
        verbose_name=u'Collection',
        help_text=u'Picture is included in this collection',
    )
    order = models.IntegerField(
        verbose_name=u'Order',
        help_text=u'What order to display this picture within the collection.',
        max_length=255
    )

    class Meta:
        verbose_name = u"Collection Picture"
        verbose_name_plural = u"Collection Pictures"
        ordering = ['order', ]

    def __unicode__(self):
        return self.picture.name + " is displayed in " + self.collection.name + (
        " in position %d" % self.order)


class Collection(models.Model):
    pictures = models.ManyToManyField(Picture, through='CollectionPictures', null=True)
    [... Bunch of irrelevant stuff after]
Run Code Online (Sandbox Code Playgroud)

因此,如果我不必迁移旧数据,这应该有效(模型中唯一的区别是它没有 通过='CollectionPictures'

这是我的迁移:

class Migration(migrations.Migration):

    dependencies = [
        ('artist', '0002_auto_20141013_1451'),
        ('business', '0001_initial'),
    ]

    operations = [
        migrations.CreateModel(
            name='CollectionPictures',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('order', models.IntegerField(help_text='What order to display this picture within the collection.', max_length=255, verbose_name='Order')),
                ('collection', models.ForeignKey(verbose_name='Collection', to='business.Collection', help_text='Picture is included in this collection')),
                ('picture', models.ForeignKey(verbose_name='Picture', to='artist.Picture', help_text='Picture is included in this collection.')),
            ],
            options={
                'ordering': ['order'],
                'verbose_name': 'Collection Picture',
                'verbose_name_plural': 'Collection Pictures',
            },
            bases=(models.Model,),
        ),
        migrations.AlterField(
            model_name='collection',
            name='pictures',
            field=models.ManyToManyField(to=b'artist.Picture', null=True, through='business.CollectionPictures'),
        ),
    ]
Run Code Online (Sandbox Code Playgroud)

这在迁移时会引发错误:

ValueError:无法将字段business.Collection.pictures更改为business.Collection.pictures - 它们不是兼容类型(您无法更改到M2M字段或从M2M字段更改,或在M2M字段上添加或删除)

有没有人已经尝试过这种新的1.7迁移操作?

谢谢 !

Kev*_*nry 22

最安全的方法是创建一个新字段并复制数据.

  1. pictures单独留下并添加pictures2你的through字段.跑makemigrations.

  2. 编辑生成的迁移文件,并添加一个RunPython命令,将数据从旧表复制到新表.也许您可以以编程方式为新order列选择一个好的值.

  3. 删除旧pictures字段.跑makemgirations.

  4. 重命名pictures2pictures.跑makemigrations.

这种方法应该使您处于所需的状态,并保持数据的完整性.

如果复制数据是一个大问题,你可以尝试别的东西,比如order在SQL中添加列,使用db_table选项on CollectionPictures使其指向现有表,然后擦除迁移和重做--fake.但这似乎比上述方法更具风险.


Ash*_*lor 5

老问题,但我也遇到了这个问题,我找到了一种适用于 Django 1.11 的方法,并且也应该适用于旧版本。所需的类存在回到 1.7 并且仍然存在于 2.0

修复涉及使用SeparateDatabaseAndState迁移类手动更改迁移以执行我们想要的操作。这个类让 Django 更新状态,但让我们控制要执行的操作。在这种情况下,我们只想重命名模型表,其他一切都已经设置好了。

步骤:

  1. 创建新的 ManyToMany Through 模型,但指定自定义表名,并且没有额外的字段:

    class CollectionPictures(models.Model):
        collection = ...
        picture = ...
        class Meta:
            # Change myapp to match.
            db_table = "myapp_collection_pictures"
            unique_together = (("collection", "picture"))
    
    Run Code Online (Sandbox Code Playgroud)
  2. 采用现有的迁移,并采用它生成的操作并将其全部包装在一个 new 中SeparateDatabaseAndState

    class Migration(migrations.Migration):
    
        dependencies = [
            ('artist', '0002_auto_20141013_1451'),
            ('business', '0001_initial'),
        ]
    
        operations = [
            migrations.SeparateDatabaseAndState(
                database_operations=[
                ],
                state_operations=[
                    migrations.CreateModel(
                        name='CollectionPictures',
                        fields=[
                            ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                            ('order', models.IntegerField(help_text='What order to display this picture within the collection.', max_length=255, verbose_name='Order')),
                            ('collection', models.ForeignKey(verbose_name='Collection', to='business.Collection', help_text='Picture is included in this collection')),
                            ('picture', models.ForeignKey(verbose_name='Picture', to='artist.Picture', help_text='Picture is included in this collection.')),
                        ],
                        options={
                            'ordering': ['order'],
                            'verbose_name': 'Collection Picture',
                            'verbose_name_plural': 'Collection Pictures',
                        },
                        bases=(models.Model,),
                    ),
                    migrations.AlterField(
                        model_name='collection',
                        name='pictures',
                        field=models.ManyToManyField(to=b'artist.Picture', null=True, through='business.CollectionPictures'),
                    ),
                ]
            )
    
    Run Code Online (Sandbox Code Playgroud)
  3. db_table从类 Meta 中删除,并在SeparateDatabaseAndState,之后添加此操作(而不是进入 database_operations。):

    migrations.AlterModelTable(
        name='collectionpicture',
        table=None,
    ),
    
    Run Code Online (Sandbox Code Playgroud)

现在,如果你运行`./mange.py sqlmigrate myapp 0003(选择正确的数字前缀!)你应该幸运地看到这样的输出:

class CollectionPictures(models.Model):
    collection = ...
    picture = ...
    class Meta:
        # Change myapp to match.
        db_table = "myapp_collection_pictures"
        unique_together = (("collection", "picture"))
Run Code Online (Sandbox Code Playgroud)
  1. 添加您的新列(在本例中为“order”)并创建一个新的迁移。可能可以同时执行此操作,但我认为在两次迁移中执行此操作更容易。

(如果您乐于将自定义表名保留在那里,则不严格要求第 3 步。)

并仔细检查./manage.py makemigrations --check- 它应该打印“未检测到更改”。