Ошибка миграции Django: вы не можете вносить изменения в поля M2M или из них, а также добавлять или удалять сквозные = в полях M2M

Я пытаюсь изменить поле M2M на поле ForeignKey. Команда validate не показывает проблем, и когда я запускаю 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)

Поэтому я не могу выполнить миграцию.

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)

Это мой первый проект Django, поэтому любые предложения приветствуются.


person loar    schedule 14.11.2014    source источник
comment
Я ненавижу эту проблему.   -  person dps    schedule 27.03.2018


Ответы (9)


Я наткнулся на это, и хотя я не особо заботился о своих данных, я все же не хотел удалять всю БД. Поэтому я открыл файл миграции и изменил команду AlterField() на команды RemoveField() и AddField(), которые работали хорошо. Я потерял свои данные в конкретном поле, но больше ничего.

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'),
),
person wanaryytel    schedule 30.08.2016
comment
Отличный ответ, просто не забудьте сначала сбросить существующие данные с помощью python manage.py dumpdata app_name.ModelName, а затем перезагрузить данные в новую модель с помощью import codecs import json - person Mauricio Collazos; 09.06.2017
comment
Тот момент, когда ты приходишь через год с той же проблемой и лучшее решение написано тобой :D - person wanaryytel; 28.09.2017

НЕТ ПОТЕРИ ДАННЫХ ПРИМЕР


Я бы сказал: если машина не может что-то сделать за нас, то давайте ей поможем!

Поскольку проблема, которую поставил здесь OP, может иметь несколько мутаций, я попытаюсь объяснить, как бороться с такой проблемой простым способом.

Предположим, у нас есть модель (в приложении под названием users), например:

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

но через некоторое время нам нужно добавить дату присоединения члена. Итак, мы хотим этого:

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

Теперь, как правило, вы столкнетесь с той же проблемой, что и OP. Чтобы решить эту проблему, выполните следующие действия:

  • начать с этого момента:

    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
    
  • создайте сквозную модель и запустите python manage.py makemigrations (но пока не добавляйте свойство through в поле Group.members):

    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()
    
  • создайте пустую миграцию с помощью команды python manage.py makemigrations users --empty и создайте скрипт преобразования в python (подробнее о миграции python здесь), который создает новые отношения (Membership) для старого поля (Group.members). Это может выглядеть так:

    # 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),
        ]
    
  • удалите поле members в модели Group и запустите python manage.py makemigrations, чтобы наш Group выглядел так:

    class Group(models.Model):
        name = models.CharField(max_length=128)
    
  • добавьте поле members в модель Group, но теперь со свойством through и запустите python manage.py makemigrations:

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

и это все!

Теперь вам нужно изменить создание членов в вашем коде по-новому - по сквозной модели. Подробнее о здесь< /а>.

Вы также можете по желанию привести его в порядок, раздавив эти миграции. .

person turkus    schedule 15.07.2018
comment
Отличный и подробный совет, сработал как шарм! Я просто хотел бы добавить, что прямой импорт моделей не рекомендуется в документы в пользу AppConfig.get_model (имя_модели). Также ожидайте немного дополнительной работы, если вы уже используете поле m2m в форме. Спасибо! - person jhnwsk; 01.08.2018
comment
Вау, отличный ответ. - person madjardi; 10.12.2018
comment
Отличный ответ. Один комментарий: в вашем примере вам также не нужно вызывать apps.get_model() для членства? - person Scott Talbert; 19.12.2018
comment
Я думаю, что это лучший ответ. Один комментарий к следующему. Поскольку после этого будет разбросано много файлов миграции, я предлагаю включить абзац, чтобы сообщить другим, чтобы они сжимали миграции в один, чтобы все было в порядке. - person Mond Wan; 05.07.2019
comment
@MondWan, это отличное предложение! Отредактировано. - person turkus; 07.07.2019
comment
Любые советы о том, что делать, если этот пример вызывает, например. members исчезать из пользовательского интерфейса администратора, когда я просматриваю Group через панель Django /admin/? Раньше я мог редактировать участников группы непосредственно через представление сведений о группе в пользовательском интерфейсе администратора, тогда как теперь я могу редактировать его, только создавая или удаляя членство. Есть ли способ заставить пользовательский интерфейс снова появиться в интерфейсе администратора группы? - person csvoss; 24.02.2020
comment
@csvoss не нуждался в этом. Это может быть отдельный вопрос. В любом случае вы можете добавить сквозную модель в панель администратора рядом с моделями Member и Group. - person turkus; 16.03.2020
comment
@turkus У меня та же проблема, что и у @csvoss. Я выполнил все шаги, но когда я захожу в /admin/myapp/group/xxx/change, я получаю Key 'members not found in 'GroupForm' - person bit; 23.03.2020
comment
@bit Я смог эффективно решить эту проблему, кстати! Вы должны включить Django Admin View в своей новой промежуточной модели M2M, но как только вы это сделаете, вы сможете напрямую участвовать в модели. - person csvoss; 25.03.2020
comment
Наконец, я решил добавить членство как TabularInline и использовать autocomplete_fields - person bit; 25.03.2020

Возможные обходные пути:

  • Создайте новое поле с отношением ForeignKey с именем profiles1 и НЕ изменяйте profiles. Сделайте и запустите миграцию. Вам может понадобиться параметр related_name для предотвращения конфликтов. Выполните последующую миграцию, которая удаляет исходное поле. Затем выполните еще одну миграцию, которая переименует profiles1 обратно в profiles. Очевидно, что у вас не будет данных в новом поле ForeignKey.

  • Напишите пользовательскую миграцию: https://docs.djangoproject.com/en/1.7/ref/migration-operations/

Возможно, вы захотите использовать makemigration и migration, а не syncdb.

Есть ли в вашем InstituteStaff данные, которые вы хотите сохранить?

person Paul Wolf    schedule 02.12.2014
comment
Хорошо... Я боролся с этим. У меня был отличный отчет о том, что казалось месяцами без ошибок миграции, и, наконец, это поразило меня. Я собирался уничтожить свою базу данных и начать все заново, пока не нашел это. отличная работа вокруг +1 - person nicorellius; 11.06.2015
comment
Я бы сделал это, а затем добавил миграцию данных, чтобы скопировать данные из старого поля в новое, удалить старое и объединить их все в одну миграцию. Чтобы сохранить существующее имя поля, сначала переименуйте исходное поле. - person steveayre; 02.02.2017
comment
Мне помогло просто переименовать поле с изменениями (не забудьте удалить файл миграции) - person Boris Paschenko; 24.10.2017
comment
Это то, что мне нравится. Простые, терпеливые ответы. - person MagicLAMP; 20.06.2018

Если вы все еще разрабатываете приложение и вам не нужно сохранять существующие данные, вы можете обойти эту проблему, выполнив следующие действия:

  1. Удалите и заново создайте БД.

  2. перейдите в папку вашего проекта/приложения/миграции

  3. Удалите все в этой папке, кроме файла init.py. Убедитесь, что вы также удалили каталог pycache.

  4. Запустите syncdb, сделайте миграции и выполните миграцию.

person user3136977    schedule 11.01.2015
comment
Поскольку я так привык вносить изменения в базу данных вручную, я не знал / не обращал внимания на каталог миграции и файлы, которые добавляются при запуске makemigrations. Спасибо! - person ngoue; 14.01.2015

Эта ссылка поможет вам решить все проблемы, связанные с этим Тот, который работал у меня, это python3 backend/manage.py migrate --fake app_name

person Abednego    schedule 08.09.2020
comment
Ваш ответ должен быть автономным, хорошо, если вы дадите ссылку на источник, но вы должны хотя бы процитировать решение, которое сработало для вас. - person guival; 13.04.2021

У меня буквально была одна и та же ошибка в течение нескольких дней, и я пробовал все, что видел здесь, но все равно не работал. Вот что сработало для меня:

  • Я удалил все файлы в папке миграции, кроме init.py.
  • Я также удалил свою базу данных, в моем случае это был предустановленный db.sqlite3. После этого я написал свои модели с нуля, хотя ничего не менял, но написал снова.
  • Затем примените миграции к моделям, и на этот раз все сработало и без ошибок.
person Jyotiprakash Jena    schedule 23.09.2020

Другой подход, который работал для меня:

  • Удалите существующее поле M2M и запустите миграцию.
  • Добавьте поле FK и снова запустите миграцию.

Добавленное в этом случае поле FK не имеет отношения к ранее использовавшемуся полю M2M и, следовательно, не должно создавать никаких проблем.

person Shreyas Shetty    schedule 14.03.2021

У меня была та же проблема, и я нашел это Как перенести "сквозную" связь в отношение "многие ко многим" в Django, которая действительно очень помогла мне решить эту проблему. Пожалуйста, посмотрите. Я резюмирую его ответ здесь,

Существует три модели, и одна (CollectionProduct) будет связывать отношения «многие ко многим».

введите здесь описание изображения

Это окончательный вывод,

class Product(models.Model):
    pass


class Collection(models.Model):
    products = models.ManyToManyField(
        Product,
        blank=True,
        related_name="collections",
        through="CollectionProduct",
        through_fields=["collection", "product"],
    )


class CollectionProduct(models.Model):
    collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)

    class Meta:
        db_table = "product_collection_products"

и вот решение,

Решение

Возьмите метку вашего приложения (имя пакета, например, «продукт») и имя вашего поля M2M и объедините их вместе с символом подчеркивания:

APPLABEL + _ + M2M TABLE NAME + _ + M2M FIELD NAME

Например, в нашем случае это:

product_collection_products

Это имя вашей таблицы M2M через базу данных. Теперь вам нужно отредактировать сквозную модель M2M следующим образом:


Также найдено другое решение в в Django нельзя добавлять или удалять через = в полях M2M статью, которая собирается редактировать файлы миграции. Я не пробовал это, но посмотрите, если у вас нет другого решения.

person Kushan Gunasekera    schedule 07.12.2020
comment
Я получаю сообщение об уже существующей ошибке при переносе в базу данных. - person Shreyas Shetty; 13.03.2021

Это сработало и для меня

  1. Удалить последние миграции
  2. выполнить команду python manage.py migrate --fake <application name>
  3. запустить команду 'python manage.py makemigrations'
  4. запустить команду «python manage.py migrate»

Надеюсь, это решит вашу проблему с удалением базы данных/миграций.

person MARK I    schedule 13.04.2021