更改模型以添加“通过”关系以订购多对多字段 - Django 1.7 迁移修改
Posted
技术标签:
【中文标题】更改模型以添加“通过”关系以订购多对多字段 - Django 1.7 迁移修改【英文标题】:Alter model to add "through" relationship to order a ManytoMany field - Django 1.7 migration modification 【发布时间】:2014-12-08 12:08:54 【问题描述】:我正在尝试向我不久前创建的 ManyToMany 字段添加订单。我基本上想在图片集中订购图片。我在 Django 1.7 上运行,所以不再向南迁移(我试图按照本教程进行操作:http://mounirmesselmeni.github.io/2013/07/28/migrate-django-manytomany-field-to-manytomany-through-with-south/)
这是我的“直通”关系:
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]
因此,如果我不必迁移旧数据,这个应该可以工作(模型中唯一的区别是它没有 through='CollectionPictures' em>
这是我的迁移:
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'),
),
]
这会在迁移时引发错误:
ValueError:无法将字段 business.Collection.pictures 更改为 business.Collection.pictures - 它们不是兼容的类型(您 无法更改 M2M 字段或从 M2M 字段更改,或通过 M2M 上的 = 添加或删除 字段)
有人已经尝试过使用新的 1.7 迁移进行这种操作吗?
谢谢!
【问题讨论】:
只是一些类似问题的链接:***.com/q/33257530、***.com/q/11466358、***.com/q/6063357 【参考方案1】:最安全的方法是创建一个新字段并复制数据。
不理会pictures
,并在through
字段中添加pictures2
。运行makemigrations
。
编辑生成的迁移文件并添加RunPython
命令,将数据从旧表复制到新表。也许您也可以通过编程方式为新的order
列选择一个合适的值。
删除旧的 pictures
字段。运行makemgirations
。
将pictures2
重命名为pictures
。运行makemigrations
。
这种方法应该使您的数据保持在您想要的状态。
如果复制数据是一个大问题,您可以尝试其他方法,例如在 SQL 中添加 order
列,使用 CollectionPictures
上的 db_table
选项使其指向现有表,然后擦除使用--fake
进行迁移和重做。但这似乎比上述方法风险更大。
【讨论】:
我将此标记为正确答案,因为这是我在主题得到回答之前最终所做的。如果有人踩到这一步,这里有一项工作正在进行中 django-sortedm2m github.com/gregmuellegger/django-sortedm2m/issues/7 以不同的顺序做会有什么危险吗? 2、3、1,所以我们可以跳过第 4 步? @djvg:我不确定你的建议是什么。上面的第 2 步取决于第 1 步,所以它不能在前面出现。您想避免重命名吗? 是的,确实:假设“CollectionPictures”表已首先迁移,我们可以从隐式直通表到这个新表的数据迁移开始。然后我们删除“图片”字段,而不先创建“图片2”。迁移之后,我们可以使用“through=CollectionPictures”添加一个新的“图片”字段。听起来可以吗?【参考方案2】:老问题,但我也遇到了这个问题,我找到了一种适用于 Django 1.11 的方法,并且也应该适用于旧版本。所需的类存在于 1.7 中,并且仍然存在于 2.0 中
修复涉及使用SeparateDatabaseAndState 迁移类手动更改迁移以执行我们想要的操作。这个类让 Django 更新状态,但让我们控制要执行的操作。在这种情况下,我们只想重命名模型表,其他所有内容都已正确设置。
步骤:
创建新的 ManyToMany Through 模型,但指定自定义表名,并且没有额外的字段:
class CollectionPictures(models.Model):
collection = ...
picture = ...
class Meta:
# Change myapp to match.
db_table = "myapp_collection_pictures"
unique_together = (("collection", "picture"))
采用现有迁移,并采用它生成的操作并将其全部包装在一个新的 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'),
),
]
)
从类 Meta 中删除 db_table
,并在 SeparateDatabaseAndState
之后添加此操作,(而不是进入 database_operations。):
migrations.AlterModelTable(
name='collectionpicture',
table=None,
),
现在,如果您运行 `./mange.py sqlmigrate myapp 0003(选择正确的数字前缀!),您应该幸运地看到这样的输出:
BEGIN;
--
-- Custom state/database change combination
--
--
-- Rename table for collection[Pictures to None
--
ALTER TABLE "myapp_collection_pictures" RENAME TO "myapp_collectionpictures";
COMMIT;
-
添加新列(在本例中为“订单”)并创建新迁移。可能可以同时执行此操作,但我认为在两次迁移中执行此操作会更容易。
(如果您愿意将自定义表名保留在此处,则不严格要求第 3 步。)
并仔细检查./manage.py makemigrations --check
-- 它应该打印“未检测到更改”。
【讨论】:
以上是关于更改模型以添加“通过”关系以订购多对多字段 - Django 1.7 迁移修改的主要内容,如果未能解决你的问题,请参考以下文章