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

loa*_*oar 47 migration django foreign-keys 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)
Run Code Online (Sandbox Code Playgroud)

所以我无法进行迁移.

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)
Run Code Online (Sandbox Code Playgroud)

这是我的第一个Django项目,所以欢迎任何建议.

wan*_*tel 59

我偶然发现了这一点,虽然我并不关心我的数据,但我仍然不想删除整个数据库.所以我打开了迁移文件并将AlterField()命令更改为a RemoveField()和一个AddField()运行良好的命令.我丢失了关于特定字段的数据,但没有别的.

migrations.AlterField(
    model_name='player',
    name='teams',
    field=models.ManyToManyField(related_name='players', through='players.TeamPlayer', to='players.Team'),
),
Run Code Online (Sandbox Code Playgroud)

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'),
),
Run Code Online (Sandbox Code Playgroud)

  • 一年之后,当你遇到同样的问题和最好的解决方案时,你写的那一刻:D (26认同)
  • 优秀的答案,只是不会忘记首先使用python manage.py dumpdata app_name.ModelName转储现有数据然后使用import codecs import json将数据重新加载到新模型 (3认同)

tur*_*kus 22

我想说:如果机器不能为我们做点什么,那就让我们帮忙吧!

因为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
Run Code Online (Sandbox Code Playgroud)

但过了一段时间我们需要添加成员加入的日期.所以我们想要这个:

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()
Run Code Online (Sandbox Code Playgroud)

现在,通常你会遇到与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
    
    Run Code Online (Sandbox Code Playgroud)
  • 通过建立模型和运行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()
    
    Run Code Online (Sandbox Code Playgroud)
  • 使用python manage.py makemigrations users --empty命令创建一个空迁移并在python中创建转换脚本(更多关于这里的python迁移),它Membership为旧字段(Group.members)创建新的relationship ().它可能看起来像这样:

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

    class Group(models.Model):
        name = models.CharField(max_length=128)
    
    Run Code Online (Sandbox Code Playgroud)
  • 添加members字段Group模型,但现在使用through属性并运行python manage.py makemigrations:

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

就是这样!现在,您需要在代码中以新的方式更改成员的创建 - 通过模型.更多关于这里.

  • @bit 我能够有效地解决这个问题,顺便说一句!您必须在新的 M2M 中介模型上启用 Django 管理视图,但一旦完成此操作,您就可以直接成为该模型的成员。 (2认同)
  • 最后我解决了将 Membership 添加为 TabularInline 并使用 `autocomplete_fields` (2认同)

小智 21

可能的解决方法:

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

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

您可能需要使用makemigrationmigration,而不是syncdb.

您是否InstituteStaff拥有要保留的数据?


小智 16

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

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

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

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

  4. 运行syncdb,makemigrations和migrate.