web-dev-qa-db-ja.com

Django移行エラー:M2Mフィールドとの間で変更したり、M2Mフィールドでthrough =を追加または削除することはできません

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プロジェクトなので、どんな提案でも歓迎します。

44
loar

私はこれにつまずいて、自分のデータについてはあまり気にしませんでしたが、DB全体を削除したくはありませんでした。そこで、移行ファイルを開いて、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'),
),
56
wanaryytel

潜在的な回避策:

  • profiles1というForeignKey関係を持つ新しいフィールドを作成し、profilesを変更しないでください。移行を作成して実行します。競合を防ぐために、related_nameパラメーターが必要になる場合があります。元のフィールドを削除する後続の移行を実行します。次に、profiles1の名前をprofilesに戻す別の移行を実行します。明らかに、新しいForeignKeyフィールドにはデータがありません。

  • カスタム移行を作成します: https://docs.djangoproject.com/en/1.7/ref/migration-operations/

makemigrationではなく、migrationsyncdbを使用することもできます。

InstituteStaffには保持するデータがありますか?

21
Paul Wolf

データ損失なしの例


私は言うだろう:マシンが私たちのために何かをすることができない場合、それを助けましょう!

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を実行しますが、Group.membersフィールドにthroughプロパティをまだ入れないでください ):

    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 migrations 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),
        ]
    
  • 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')
    

以上です!

ここで、モデル内で、コードの新しい方法でメンバーの作成を変更する必要があります。 こちら の詳細。

squashing これらの移行により、オプションで整理することもできます。

18
turkus

まだアプリケーションを開発していて、既存のデータを保存する必要がない場合は、次の方法でこの問題を回避できます。

  1. データベースを削除して再作成します。

  2. project/app/migrationsフォルダーに移動します

  3. init。pyファイルを除き、そのフォルダー内のすべてを削除します。 pycachedirも必ず削除してください。

  4. Syncdb、makemigrations、および移行を実行します。

16
user3136977