Django migration error :you cannot alter to or from M2M fields, or add or remove through= on M2M fields Django migration error :you cannot alter to or from M2M fields, or add or remove through= on M2M fields django django

Django migration error :you cannot alter to or from M2M fields, or add or remove through= on M2M fields


I stumbled upon this and although I didn't care about my data much, I still didn't want to delete the whole DB. So I opened the migration file and changed the AlterField() command to a RemoveField() and an AddField() command that worked well. I lost my data on the specific field, but nothing else.

I.e.

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

to

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'),),


NO DATA LOSS EXAMPLE


I would say: If machine cannot do something for us, then let's help it!

Because the problem that OP put here can have multiple mutations, I will try to explain how to struggle with that kind of problem in a simple way.

Let's assume we have a model (in the app called users) like this:

from django.db import modelsclass Person(models.Model):    name = models.CharField(max_length=128)    def __str__(self):        return self.nameclass Group(models.Model):    name = models.CharField(max_length=128)    members = models.ManyToManyField(Person)    def __str__(self):        return self.name

but after some while we need to add a date of a member join. So we want this:

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 itselfclass Membership(models.Model):    person = models.ForeignKey(Person, on_delete=models.CASCADE)    group = models.ForeignKey(Group, on_delete=models.CASCADE)    date_joined = models.DateField()

Now, normally you will hit the same problem as OP wrote. To solve it, follow these steps:

  • start from this point:

    from django.db import modelsclass Person(models.Model):    name = models.CharField(max_length=128)    def __str__(self):        return self.nameclass Group(models.Model):    name = models.CharField(max_length=128)    members = models.ManyToManyField(Person)    def __str__(self):        return self.name
  • create through model and run python manage.py makemigrations (but don't put through property in the Group.members field yet):

    from django.db import modelsclass Person(models.Model):    name = models.CharField(max_length=128)    def __str__(self):        return self.nameclass Group(models.Model):    name = models.CharField(max_length=128)    members = models.ManyToManyField(Person) # <-- no through property yet!    def __str__(self):        return self.nameclass 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()
  • create an empty migration using python manage.py makemigrations users --empty command and create conversion script in python (more about the python migrations here) which creates new relations (Membership) for an old field (Group.members). It could look like this:

    # Generated by Django A.B on YYYY-MM-DD HH:MMimport datetimefrom django.db import migrationsdef 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),    ]
  • remove members field in the Group model and run python manage.py makemigrations, so our Group will look like this:

    class Group(models.Model):    name = models.CharField(max_length=128)
  • add members field the the Group model, but now with through property and run python manage.py makemigrations:

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

and that's it!

Now you need to change creation of members in a new way in your code - by through model. More about here.

You can also optionally tidy it up, by squashing these migrations.


Potential workarounds:

  • Create a new field with the ForeignKey relationship called profiles1 and DO NOT modify profiles. Make and run the migration. You might need a related_name parameter to prevent conflicts. Do a subsequent migration that drops the original field. Then do another migration that renames profiles1 back to profiles. Obviously, you won't have data in the new ForeignKey field.

  • Write a custom migration: https://docs.djangoproject.com/en/1.7/ref/migration-operations/

You might want to use makemigration and migration rather than syncdb.

Does your InstituteStaff have data that you want to retain?