问题
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 into yyy they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)
So I can't make the migration.
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)
This is my first Django project so any suggestions are welcome.
回答1:
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'),
),
回答2:
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 putthrough
property in theGroup.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 theGroup
model and runpython manage.py makemigrations
, so ourGroup
will look like this:class Group(models.Model): name = models.CharField(max_length=128)
add
members
field the theGroup
model, but now withthrough
property and runpython 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.
回答3:
Potential workarounds:
Create a new field with the ForeignKey relationship called
profiles1
and DO NOT modifyprofiles
. Make and run the migration. You might need arelated_name
parameter to prevent conflicts. Do a subsequent migration that drops the original field. Then do another migration that renamesprofiles1
back toprofiles
. 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?
回答4:
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:
Delete and re-create the db.
go to your project/app/migrations folder
Delete everything in that folder with the exception of the init.py file. Make sure you also delete the pycache dir.
Run syncdb, makemigrations, and migrate.
来源:https://stackoverflow.com/questions/26927705/django-migration-error-you-cannot-alter-to-or-from-m2m-fields-or-add-or-remove