web-dev-qa-db-fra.com

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

J'essaie de modifier un champ M2M en un champ ForeignKey. La commande validate ne me montre aucun problème et lorsque 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)

Je ne peux donc pas effectuer 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)

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

44
loar

Je suis tombé sur cela et bien que je ne me soucie pas beaucoup de mes données, je ne voulais toujours pas supprimer la base de données entière. J'ai donc ouvert le fichier de migration et changé la commande AlterField() en RemoveField() et AddField() qui fonctionnait bien. J'ai perdu mes données sur le domaine spécifique, mais rien d'autre.

C'est à dire.

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

Solutions de contournement potentielles:

  • Créez un nouveau champ avec la relation ForeignKey appelée profiles1 et NE modifiez PAS profiles. Effectuez et exécutez la migration. Vous pourriez avoir besoin d'un related_name paramètre pour éviter les conflits. Effectuez une migration ultérieure qui supprime le champ d'origine. Effectuez ensuite une autre migration qui renomme profiles1 retour à profiles. De toute évidence, vous n'aurez pas de données dans le nouveau champ ForeignKey.

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

Vous souhaiterez peut-être utiliser makemigration et migration plutôt que syncdb.

Votre InstituteStaff contient-elle des données que vous souhaitez conserver?

21
Paul Wolf

AUCUN EXEMPLE DE PERTE DE DONNÉES


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

Parce que le problème que OP pose ici peut avoir plusieurs mutations, je vais essayer d'expliquer comment lutter avec ce type de problème de manière simple.

Supposons que nous ayons 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 un certain temps, nous devons ajouter une date de membre. 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 rencontrerez le même problème que l'OP a écrit. Pour le résoudre, procédez comme suit:

  • 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 via le modèle et exécuter python manage.py makemigrations ( mais ne mettez pas la propriété through dans le Group.members champ encore ):

    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 à l'aide de python makemigrations users --empty commande et créer un script de conversion dans python (plus sur les migrations python ici ) qui crée de nouvelles relations (Membership) pour un ancien champ (Group.members). Cela 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')
        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),
        ]
    
  • supprimer le champ members dans le modèle Group et exécuter python manage.py makemigrations, donc notre Group ressemblera à ceci:

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

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

et c'est tout!

Vous devez maintenant modifier la création de membres d'une nouvelle manière dans votre code - par le biais du modèle. En savoir plus sur ici .

Vous pouvez également éventuellement le ranger, en écraser ces migrations.

18
turkus

Si vous développez toujours l'application et n'avez pas besoin de conserver vos données existantes, vous pouvez contourner ce problème en procédant comme suit:

  1. Supprimez et recréez la base de données.

  2. allez dans votre dossier project/app/migrations

  3. Supprimez tout dans ce dossier à l'exception du fichier init. Py. Assurez-vous de supprimer également le répertoire pycache .

  4. Exécutez syncdb, makemigrations et migrez.

16
user3136977