Django 迁移错误:您无法更改 M2M 字段或从 M2M 字段更改,或通过 = 在 M2M 字段上添加或删除
Posted
技术标签:
【中文标题】Django 迁移错误:您无法更改 M2M 字段或从 M2M 字段更改,或通过 = 在 M2M 字段上添加或删除【英文标题】:Django migration error :you cannot alter to or from M2M fields, or add or remove through= on M2M fields 【发布时间】:2015-01-11 17:01:43 【问题描述】:我正在尝试将 M2M 字段修改为 ForeignKey 字段。命令 validate 显示没有问题,当我运行 syncdb 时:
ValueError: Cannot alter field xxx into yyy they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)
所以我无法进行迁移。
class InstituteStaff(Person):
user = models.OneToOneField(User, blank=True, null=True)
investigation_area = models.ManyToManyField(InvestigationArea, blank=True,)
investigation_group = models.ManyToManyField(InvestigationGroup, blank=True)
council_group = models.ForeignKey(CouncilGroup, null=True, blank=True)
#profiles = models.ManyToManyField(Profiles, null = True, blank = True)
profiles = models.ForeignKey(Profiles, null = True, blank = True)
这是我的第一个 Django 项目,欢迎提出任何建议。
【问题讨论】:
我讨厌这个问题。 【参考方案1】:我偶然发现了这一点,虽然我不太关心我的数据,但我仍然不想删除整个数据库。所以我打开了迁移文件并将AlterField()
命令更改为RemoveField()
和AddField()
命令,效果很好。我丢失了我在特定领域的数据,但没有别的。
即
migrations.AlterField(
model_name='player',
name='teams',
field=models.ManyToManyField(related_name='players', through='players.TeamPlayer', to='players.Team'),
),
到
migrations.RemoveField(
model_name='player',
name='teams',
),
migrations.AddField(
model_name='player',
name='teams',
field=models.ManyToManyField(related_name='players', through='players.TeamPlayer', to='players.Team'),
),
【讨论】:
很好的答案,只是不要忘记先用 python manage.py dumpdata app_name.ModelName 转储现有数据,然后用 import codecs import json 将数据重新加载到新模型【参考方案2】:无数据丢失示例
我想说:如果机器不能为我们做点什么,那就让我们来帮助它吧!
因为 OP 放在这里的问题可以有多个突变,我将尝试以简单的方式解释如何解决这种问题。
假设我们有一个这样的模型(在名为 users
的应用中):
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person)
def __str__(self):
return self.name
但过了一段时间我们需要添加成员加入的日期。所以我们想要这个:
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership') # <-- through model
def __str__(self):
return self.name
# and through Model itself
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
现在,通常您会遇到与 OP 所写相同的问题。要解决它,请按照下列步骤操作:
从这一点开始:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person)
def __str__(self):
return self.name
通过模型创建并运行python manage.py makemigrations
(但不要将through
属性放在Group.members
字段中):
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person) # <-- no through property yet!
def __str__(self):
return self.name
class Membership(models.Model): # <--- through model
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
使用python manage.py makemigrations users --empty
命令创建一个空迁移并在python 中创建转换脚本(更多关于python 迁移here),它为旧字段(Group.members
)创建新关系(Membership
)。它可能看起来像这样:
# Generated by Django A.B on YYYY-MM-DD HH:MM
import datetime
from django.db import migrations
def create_through_relations(apps, schema_editor):
Group = apps.get_model('users', 'Group')
Membership = apps.get_model('users', 'Membership')
for group in Group.objects.all():
for member in group.members.all():
Membership(
person=member,
group=group,
date_joined=datetime.date.today()
).save()
class Migration(migrations.Migration):
dependencies = [
('myapp', '0005_create_models'),
]
operations = [
migrations.RunPython(create_through_relations, reverse_code=migrations.RunPython.noop),
]
删除Group
模型中的members
字段并运行python manage.py makemigrations
,所以我们的Group
将如下所示:
class Group(models.Model):
name = models.CharField(max_length=128)
在Group
模型中添加members
字段,但现在使用through
属性并运行python manage.py makemigrations
:
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
就是这样!
现在您需要在代码中以新的方式更改成员的创建方式 - 通过模型。更多关于here。
您还可以选择通过squashing 这些迁移来整理它。
【讨论】:
伟大而彻底的建议,就像一个魅力!我想补充一点,在docs 中不鼓励直接导入模型,而支持AppConfig.get_model(model_name)。如果您已经在表单中使用 m2m 字段,还需要做一些额外的工作。谢谢! 哇,答案太棒了。 如果这个例子引起了任何关于如何做的建议,例如当我通过 Django/admin/
面板查看 Group
时,members
从管理 UI 中消失?以前我可以直接通过管理 UI 中组的详细视图来编辑组的成员,而现在我只能通过创建或删除成员资格来编辑它。有什么方法可以让 UI 重新出现在群组的管理界面上?
@bit 我能够有效地解决这个问题,顺便说一句!您必须在新的 M2M 中间模型上启用 Django Admin View,但是一旦完成,您就可以直接成为模型的成员。
最后我解决了将 Membership 添加为 TabularInline 并使用 autocomplete_fields
【参考方案3】:
可能的解决方法:
使用名为 profiles1
的 ForeignKey 关系创建一个新字段,并且不要修改 profiles
。制作并运行迁移。您可能需要一个 related_name
参数来防止冲突。执行删除原始字段的后续迁移。然后进行另一次迁移,将profiles1
重命名回profiles
。显然,新的 ForeignKey 字段中不会有数据。
编写自定义迁移:https://docs.djangoproject.com/en/1.7/ref/migration-operations/
您可能想要使用makemigration
和migration
而不是syncdb
。
您的InstituteStaff
是否有您想要保留的数据?
【讨论】:
很好......我一直在努力解决这个问题。我有一个很好的记录,感觉几个月没有迁移错误,最后这打击了我。我正要销毁我的数据库并重新开始,直到我发现这个。 +1 的出色工作 我会这样做,然后添加数据迁移以将数据从旧字段复制到新字段,删除旧字段,然后将它们全部压缩到同一个迁移中。要保留现有字段名称,请先重命名原始字段。 只需重命名字段,更改对我有帮助(不要忘记删除迁移文件) 这就是我喜欢的。简单、耐心的回答。【参考方案4】:如果您仍在开发应用程序,并且不需要保留现有数据,则可以通过执行以下操作来解决此问题:
删除并重新创建数据库。
转到您的项目/应用程序/迁移文件夹
删除该文件夹中的所有内容,init.py 文件除外。确保您还删除了 pycache 目录。
运行 syncdb、makemigrations 和 migrate。
【讨论】:
已经习惯于手动更改数据库,我不知道/忘记了运行makemigrations
时添加的迁移目录和文件。谢谢!【参考方案5】:
另一种对我有用的方法:
删除现有的 M2M 字段并运行迁移。 添加 FK 字段并再次运行迁移。在这种情况下添加的 FK 字段与之前使用的 M2M 字段无关,因此不会产生任何问题。
【讨论】:
【参考方案6】:首先删除您应用中的迁移(“迁移”下的文件夹/文件 文件夹) Showing the 'migrations' folder
然后删除'db.sqlite3'文件 Showing the 'db.sqlite3' file
然后运行python manage.py makemigrations name_of_app
终于运行python manage.py migrate
【讨论】:
【参考方案7】:This link helps you resolve all problems related to this 对我有用的是 python3 backend/manage.py migrate --fake "app_name"
【讨论】:
你的答案应该是独立的,如果你链接到源代码很好,但你至少应该引用对你有用的解决方案。【参考方案8】:几天来我确实遇到了同样的错误,我已经尝试了我在这里看到的所有东西,但仍然没有用。 这对我有用:
我删除了迁移文件夹中的所有文件,除了 init.py 我还删除了我的数据库,在我的情况下,它是预安装的 db.sqlite3 在此之后,我从头开始编写模型,虽然我没有更改任何内容,但我确实重新编写了它。 然后在模型上应用迁移,这次它可以正常工作并且没有错误。【讨论】:
【参考方案9】:我遇到了同样的问题,发现这篇How to Migrate a ‘through’ to a many to many relation in Django 文章真的帮助我解决了这个问题。请看一看。我将在这里总结他的答案,
有三个模型,一个(CollectionProduct
)将连接多对多关系。
这是最终的输出,
class Product(models.Model):
pass
class Collection(models.Model):
products = models.ManyToManyField(
Product,
blank=True,
related_name="collections",
through="CollectionProduct",
through_fields=["collection", "product"],
)
class CollectionProduct(models.Model):
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
class Meta:
db_table = "product_collection_products"
这是解决方案,
解决办法
获取您的应用标签(包名称,例如“产品”)和您的 M2M 字段名称,并将它们与下划线组合在一起:
APPLABEL + _ + M2M TABLE NAME + _ + M2M FIELD NAME
例如在我们的例子中,是这样的:
product_collection_products
这是您的 M2M 直通数据库表名。现在您需要将 M2M 的直通模型编辑为:
还在In Django you cannot add or remove through= on M2M fields 文章中找到了另一个解决方案,该解决方案将编辑迁移文件。我没有尝试过,但是如果您没有其他解决方案,请查看。
【讨论】:
迁移到数据库时出现“关系已存在”错误。【参考方案10】:这也适用于我
-
删除上次迁移
运行命令
python manage.py migrate --fake <application name>
运行命令'python manage.py makemigrations'
运行命令'python manage.py migrate'
希望这将解决您删除数据库/迁移的问题
【讨论】:
【参考方案11】:将“通过”属性添加到现有 M2M 字段时会发生这种情况: 因为 M2M 字段默认由模型处理,它们在中定义(如果设置了 through)。 尽管将 through 设置为新模型时,M2M 字段由该新模型处理,因此更改中的错误 解决方案:- 您可以重置数据库或 删除这些 m2m 字段并按照上述说明运行迁移,然后再次创建它们
【讨论】:
以上是关于Django 迁移错误:您无法更改 M2M 字段或从 M2M 字段更改,或通过 = 在 M2M 字段上添加或删除的主要内容,如果未能解决你的问题,请参考以下文章
将 m2m 更改为 char 时,Django South 迁移失败
Django - 管理员中的 UserProfile m2m 字段 - 错误