如何通过 django 中的迁移和数据将选项添加到现有的 ManyToManyField
How to add through option to existing ManyToManyField with migrations and data in django
我无法在文档或在线中找到对特定问题的引用。
我有一个现有的多对多关系。
class Books(models.Model):
name = models.CharField(max_length=100)
class Authors(models.Model):
name = models.CharField(max_length=100)
books = models.ManyToManyField(Books)
这有迁移和数据。现在我需要使用 through 选项,以便在 table 中添加一个额外的字段来保持多对多关系。
class Authorship(models.Model):
book = models.ForeignKey(Books)
author = models.ForeignKey(Authors)
ordering = models.PositiveIntegerField(default=1)
class Authors(models.Model):
name = models.CharField(max_length=100)
books = models.ManyToManyField(Books, through=Authorship)
当我 运行 迁移时,django 会为 Authorship
模型创建新的迁移。我尝试通过在 Authorship
table 中添加 ordering
列并更改 Authors
table 中的 books
列来手动创建迁移文件,但我得到了一些迁移问题。
operations = [
migrations.AddField(
model_name='authorship',
name='ordering',
field=models.PositiveIntegerField(default=1),
),
migrations.AlterField(
model_name='authors',
name='books',
field=models.ManyToManyField(to='app_name.Books', through='app_name.Authorship'),
),
]
尝试迁移时,它给出 KeyError: ('app_name', u'authorship')
我敢打赌还有其他东西受到影响,因此会出现错误。
我错过了什么?还有其他方法可以解决这个问题吗?
迁移有时会很混乱。
如果您想通过更改 m2m 字段,我建议将更改字段 Authors.books
重命名为 Authors.book
。当 makemigrations
询问时,是否将名称从 books 更改为 book? [yN]
,选择“N
”作为编号。Django 将删除 books
并创建 book
字段而不是更改。
class Authorship(models.Model):
book = models.ForeignKey("Books")
author = models.ForeignKey("Authors")
ordering = models.PositiveIntegerField(default=1)
class Authors(models.Model):
name = models.CharField(max_length=100)
book = models.ManyToManyField("Books", through="Authorship")
如果您无论如何都想使用 books
,请将 book
更改为 books
并使用 y
重复迁移过程作为 makemigrations 关于重命名问题的答案。
看起来没有办法在不进行数据迁移的情况下使用 through 选项。因此不得不求助于数据迁移方法,我从@pista329 的回答中吸取了一些想法并使用以下步骤解决了这个问题。
创建Authorship
模型
class Authorship(models.Model):
book = models.ForeignKey(Books)
author = models.ForeignKey(Authors)
ordering = models.PositiveIntegerField(default=1)
保持原始的 ManyToManyField 关系,但使用上面定义的模型添加另一个字段作为通过模型:
class Authors(models.Model):
name = models.CharField(max_length=100)
books = models.ManyToManyField(Books)
published_books = models.ManyToManyField(
to=Books,
through=Authorship,
related_name='authors_lst' # different related name is needed.
)
重要提示:您必须使用与现有 ManyToManyField 不同的 related_name
。如果不这样做,Django 可能会丢失原始字段中的数据。
添加数据迁移以将所有数据从旧 table 复制到新 Authorship
table.
在此之后,Authors
模型上的 books
字段可以删除,因为我们有一个名为 published_books
.
的新字段
有一种方法可以在不进行数据迁移的情况下添加 "through"。
我设法根据 this @MatthewWilkes' answer.
做到了
因此,要将其转换为您的数据模型:
仅使用 book
和 author
字段创建 Authorship
模型。指定 table 名称以使用与您已有的自动生成的 M2M table 相同的名称。添加 'through' 参数。
class Authorship(models.Model):
book = models.ForeignKey(Books)
author = models.ForeignKey(Authors)
class Meta:
db_table = 'app_name_authors_books'
class Authors(models.Model):
name = models.CharField(max_length=100)
books = models.ManyToManyField(Books, through=Authorship)
生成迁移,但还没有运行。
编辑生成的迁移并将迁移操作包装到 migrations. SeparateDatabaseAndState
操作中,所有操作都在 state_operations
字段中(database_operations
留空)。你最终会得到这样的结果:
operations = [
migrations.SeparateDatabaseAndState(state_operations=[
migrations.CreateModel(
name='Authorship',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('book', models.ForeignKey(to='app_name.Books')),
],
options={
'db_table': 'app_name_authors_books',
},
),
migrations.AlterField(
model_name='authors',
name='books',
field=models.ManyToManyField(through='app_name.Authorship', to='app_name.Books'),
),
migrations.AddField(
model_name='authorship',
name='author',
field=models.ForeignKey( to='app_name.Author'),
),
])
]
您现在可以 运行 迁移并将额外的 ordering
字段添加到您的 M2M table。
编辑:
显然,对于自动 M2M tables 和模型定义的 tables,数据库中的列名生成略有不同。 (我使用的是 Django 1.9.3。)
在描述的过程之后,我还必须手动将具有 2 个单词名称 (two_words=models.ForeignKey(...)
) 的字段的列名从 twowords_id
更改为 two_words_id
。
我无法在文档或在线中找到对特定问题的引用。
我有一个现有的多对多关系。
class Books(models.Model):
name = models.CharField(max_length=100)
class Authors(models.Model):
name = models.CharField(max_length=100)
books = models.ManyToManyField(Books)
这有迁移和数据。现在我需要使用 through 选项,以便在 table 中添加一个额外的字段来保持多对多关系。
class Authorship(models.Model):
book = models.ForeignKey(Books)
author = models.ForeignKey(Authors)
ordering = models.PositiveIntegerField(default=1)
class Authors(models.Model):
name = models.CharField(max_length=100)
books = models.ManyToManyField(Books, through=Authorship)
当我 运行 迁移时,django 会为 Authorship
模型创建新的迁移。我尝试通过在 Authorship
table 中添加 ordering
列并更改 Authors
table 中的 books
列来手动创建迁移文件,但我得到了一些迁移问题。
operations = [
migrations.AddField(
model_name='authorship',
name='ordering',
field=models.PositiveIntegerField(default=1),
),
migrations.AlterField(
model_name='authors',
name='books',
field=models.ManyToManyField(to='app_name.Books', through='app_name.Authorship'),
),
]
尝试迁移时,它给出 KeyError: ('app_name', u'authorship')
我敢打赌还有其他东西受到影响,因此会出现错误。
我错过了什么?还有其他方法可以解决这个问题吗?
迁移有时会很混乱。
如果您想通过更改 m2m 字段,我建议将更改字段 Authors.books
重命名为 Authors.book
。当 makemigrations
询问时,是否将名称从 books 更改为 book? [yN]
,选择“N
”作为编号。Django 将删除 books
并创建 book
字段而不是更改。
class Authorship(models.Model):
book = models.ForeignKey("Books")
author = models.ForeignKey("Authors")
ordering = models.PositiveIntegerField(default=1)
class Authors(models.Model):
name = models.CharField(max_length=100)
book = models.ManyToManyField("Books", through="Authorship")
如果您无论如何都想使用 books
,请将 book
更改为 books
并使用 y
重复迁移过程作为 makemigrations 关于重命名问题的答案。
看起来没有办法在不进行数据迁移的情况下使用 through 选项。因此不得不求助于数据迁移方法,我从@pista329 的回答中吸取了一些想法并使用以下步骤解决了这个问题。
创建
Authorship
模型class Authorship(models.Model): book = models.ForeignKey(Books) author = models.ForeignKey(Authors) ordering = models.PositiveIntegerField(default=1)
保持原始的 ManyToManyField 关系,但使用上面定义的模型添加另一个字段作为通过模型:
class Authors(models.Model): name = models.CharField(max_length=100) books = models.ManyToManyField(Books) published_books = models.ManyToManyField( to=Books, through=Authorship, related_name='authors_lst' # different related name is needed. )
重要提示:您必须使用与现有 ManyToManyField 不同的
related_name
。如果不这样做,Django 可能会丢失原始字段中的数据。添加数据迁移以将所有数据从旧 table 复制到新
Authorship
table.
在此之后,Authors
模型上的 books
字段可以删除,因为我们有一个名为 published_books
.
有一种方法可以在不进行数据迁移的情况下添加 "through"。 我设法根据 this @MatthewWilkes' answer.
做到了因此,要将其转换为您的数据模型:
仅使用
book
和author
字段创建Authorship
模型。指定 table 名称以使用与您已有的自动生成的 M2M table 相同的名称。添加 'through' 参数。class Authorship(models.Model): book = models.ForeignKey(Books) author = models.ForeignKey(Authors) class Meta: db_table = 'app_name_authors_books' class Authors(models.Model): name = models.CharField(max_length=100) books = models.ManyToManyField(Books, through=Authorship)
生成迁移,但还没有运行。
编辑生成的迁移并将迁移操作包装到
migrations. SeparateDatabaseAndState
操作中,所有操作都在state_operations
字段中(database_operations
留空)。你最终会得到这样的结果:operations = [ migrations.SeparateDatabaseAndState(state_operations=[ migrations.CreateModel( name='Authorship', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('book', models.ForeignKey(to='app_name.Books')), ], options={ 'db_table': 'app_name_authors_books', }, ), migrations.AlterField( model_name='authors', name='books', field=models.ManyToManyField(through='app_name.Authorship', to='app_name.Books'), ), migrations.AddField( model_name='authorship', name='author', field=models.ForeignKey( to='app_name.Author'), ), ]) ]
您现在可以 运行 迁移并将额外的
ordering
字段添加到您的 M2M table。
编辑: 显然,对于自动 M2M tables 和模型定义的 tables,数据库中的列名生成略有不同。 (我使用的是 Django 1.9.3。)
在描述的过程之后,我还必须手动将具有 2 个单词名称 (two_words=models.ForeignKey(...)
) 的字段的列名从 twowords_id
更改为 two_words_id
。