Erreur de migration de Django: vous ne pouvez pas modifier vers ou depuis les champs M2M, ou ajouter ou supprimer VIA= sur les champs M2M

j'essaie de modifier un champ M2M en champ ForeignKey. La commande Valider ne me montre aucun problème et quand j'exécute 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)

donc je ne peux pas faire la 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)

C'est mon premier projet Django donc toutes les suggestions sont les bienvenues.

32
demandé sur Jonhy Beebop 2014-11-14 13:20:25

4 réponses

je suis tombé sur ceci et bien que je ne me souciais pas beaucoup de mes données, Je ne voulais pas supprimer tout le DB. J'ai donc ouvert le fichier de migration et changé le AlterField() commande RemoveField() et AddField() commande qui a bien fonctionné. J'ai perdu mes données sur le terrain, mais rien d'autre.

I. E.

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'),
),
26
répondu makaveli 2016-08-30 09:34:28

solutions de rechange possibles:

  • Créer un nouveau champ avec le ForeignKey relation appelée profiles1 et ne modifiez pas profiles. Faire et exécuter la migration. Vous pourriez avoir besoin d'un related_name paramètre pour prévenir les conflits. Faire une migration subséquente qui fait tomber le champ original. Puis faites une autre migration qui renomme profiles1 retour profiles. Évidemment, vous n'aurez pas de données dans le nouveau champ ForeignKey.

  • écrire une migration personnalisée: https://docs.djangoproject.com/en/1.7/ref/migration-operations/

Vous pouvez utiliser makemigration et migration plutôt que syncdb.

votre InstituteStaff disposons de données que vous souhaitez conserver?

21
répondu Paul Wolf 2014-12-02 09:53:52

si vous êtes encore en train de développer l'application, et que vous n'avez pas besoin de préserver vos données existantes, vous pouvez contourner ce problème en faisant ce qui suit:

  1. supprimer et recréer la base de données.

  2. allez dans votre dossier projet/app/migrations

  3. supprimez tout dans ce dossier à l'exception de init.dossier py. Assurez-vous que vous supprimez aussi le pycache dir.

  4. Lancez syncdb, makemigrations, et migrate.

15
répondu user3136977 2015-01-11 18:27:52

je dirais: si machine ne peut pas faire quelque chose pour nous, alors aidons-le!

parce que le problème que OP a mis ici peut avoir de multiples mutations, je vais essayer d'expliquer comment lutter avec ce genre de problème d'une manière simple.

imaginons que nous avons un modèle (dans l'application appelée users) comme ceci:

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

mais après tout nous avons besoin d'ajouter une date d'un membre de la rejoindre. Nous voulons donc ceci:

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

Maintenant, normalement, vous j'ai eu le même problème que L'OP a écrit. Dans ce cas, on revient sur ce que j'ai écrit au début, donc on AIDE la machine. Nous pouvons atteindre cet objectif en les étapes suivantes:

  • démarrer à partir de ce 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
    
  • créer un modèle et run python manage.py makemigrations ( mais ne mettez pas through propriété Group.members terrain):

    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()
    
  • créer une migration vide en utilisant python makemigrations users --empty commande et création script de conversion en python (en savoir plus sur les migrations python ici) qui crée de nouvelles relations (Membership) pour un ancien champ (Group.members). Il pourrait ressembler à ceci:

    # 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')
        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),
        ]
    
  • supprimer members champ Group modéliser et exécuter python manage.py makemigrations, de sorte que notre Group ressemblera à ceci:

    class Group(models.Model):
        name = models.CharField(max_length=128)
    
  • ajouter members le champ Group modèle, mais maintenant, avec through propriété et courir python manage.py makemigrations:

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

et c'est tout! Maintenant, vous devez créer le changement des membres d'une manière nouvelle dans votre code par modèle. Plus sur ici.

3
répondu turkus 2018-09-01 21:46:59