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

后端 未结 7 1642
梦如初夏
梦如初夏 2020-12-12 18:48

I\'m trying to modify a M2M field to a ForeignKey field. The command validate shows me no issues and when I run syncdb :

ValueError: Cannot alter field xxx i         


        
相关标签:
7条回答
  • 2020-12-12 19:23

    This link helps you resolve all problems related to this The one which worked for me is python3 backend/manage.py migrate --fake "app_name"

    0 讨论(0)
  • 2020-12-12 19:27

    I literally had the same error for days and i had tried everything i saw here but still didn'y work. This is what worked for me:

    • I deleted all the files in migrations folder exceps init.py
    • I also deleted my database in my case it was the preinstalled db.sqlite3 After this, i wrote my models from the scratch, although i didn't change anything but i did write it again.
    • Apply migrations then on the models and this time it worked and no errors.
    0 讨论(0)
  • 2020-12-12 19:36

    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'),
    ),
    
    0 讨论(0)
  • 2020-12-12 19:37

    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?

    0 讨论(0)
  • 2020-12-12 19:39

    If you're still developing the application, and don't need to preserve your existing data, you can get around this issue by doing the following:

    1. Delete and re-create the db.

    2. go to your project/app/migrations folder

    3. Delete everything in that folder with the exception of the init.py file. Make sure you also delete the pycache dir.

    4. Run syncdb, makemigrations, and migrate.

    0 讨论(0)
  • 2020-12-12 19:44

    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 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
    

    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 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()
    

    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 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
      
    • 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 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()
      
    • 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: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),
          ]
      
    • 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.

    0 讨论(0)
提交回复
热议问题