Django迁移错误:您无法更改M2M字段或从M2M字段更改,或在M2M字段上添加或删除

时间:2014-11-14 10:20:26

标签: django foreign-keys migration manytomanyfield

我正在尝试将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项目,欢迎任何建议。

9 个答案:

答案 0 :(得分:52)

我偶然发现了这一点,虽然我并不关心我的数据,但我仍然不想删除整个数据库。所以我打开了迁移文件并将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'),
),

答案 1 :(得分:21)

潜在的解决方法:

  • 使用名为profiles1的ForeignKey关系创建一个新字段,不要修改profiles。制作并运行迁移。您可能需要related_name参数来防止冲突。执行后续迁移以删除原始字段。然后执行另一次迁移,将profiles1重命名为profiles。显然,您在新的ForeignKey字段中没有数据。

  • 撰写自定义迁移:https://docs.djangoproject.com/en/1.7/ref/migration-operations/

您可能希望使用makemigrationmigration而不是syncdb

您的InstituteStaff是否包含您要保留的数据?

答案 2 :(得分:15)

如果您仍在开发应用程序,并且不需要保留现有数据,则可以通过执行以下操作来解决此问题:

  1. 删除并重新创建数据库。

  2. 转到您的项目/ app / migrations文件夹

  3. 删除该文件夹中的所有内容,但 init .py文件除外。确保您还删除 pycache 目录。

  4. 运行syncdb,makemigrations和migrate。

答案 3 :(得分:13)

无数据丢失示例


我会说:如果机器无法为我们做点什么,那就让我们帮忙吧!

由于这里放置的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 makemigrations users --empty命令创建一个空迁移,并在python中创建转换脚本(有关python迁移here的更多信息),这将为旧字段创建新关系(Membership) (Group.members)。看起来可能像这样:

    # 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),
        ]
    
  • 删除members模型中的Group字段并运行python manage.py makemigrations,因此我们的Group如下所示:

    class Group(models.Model):
        name = models.CharField(max_length=128)
    
  • members模型中添加Group字段,但现在具有through属性并运行python manage.py makemigrations

    class Group(models.Model):
        name = models.CharField(max_length=128)
        members = models.ManyToManyField(Person, through='Membership')
    

就是这样!现在,您需要通过模型以新的方式在代码中更改成员的创建。有关here的更多信息。

答案 4 :(得分:1)

This link helps you resolve all problems related to this 对我有用的是python3 backend / manage.py migration --fake“ app_name”

答案 5 :(得分:1)

另一种对我有用的方法:

  • 删除现有的 M2M 字段并运行迁移。
  • 添加 FK 字段并再次运行迁移。

在这种情况下添加的 FK 字段与之前使用的 M2M 字段无关,因此不会产生任何问题。

答案 6 :(得分:0)

几天来,我确实遇到了同样的错误,我尝试了在这里看到的所有内容,但仍然无法正常工作。 这对我有用:

  • 我删除了迁移文件夹摘录 init .py
  • 中的所有文件
  • 我还删除了我的数据库,因为它是预装的db.sqlite3 在此之后,我从头开始编写了模型,尽管我什么都没做,但我还是写了。
  • 然后在模型上应用迁移,这次可以正常运行了,没有错误。

答案 7 :(得分:0)

我遇到了同样的问题,并且发现了这篇How to Migrate a ‘through’ to a many to many relation in Django文章,确实对我解决这个问题有很大帮助。请看一看。我在这里总结他的答案,

存在三种模型,一个(CollectionProduct)将连接多对多关系。

enter image description here

这是最终输出,

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文章中找到了另一个解决方案,该解决方案将编辑迁移文件。我没有尝试过,但是看看您是否还有其他解决方案。

答案 8 :(得分:0)

这对我也有效

  1. 删除上次迁移
  2. 运行命令 python manage.py migrate --fake <application name>
  3. 运行命令'python manage.py makemigrations'
  4. 运行命令'python manage.py migrate'

希望这能解决您删除数据库/迁移的问题