Найти - Пользователи
Полная версия: [Решено] Не могу произвести migrate - django.db.migrations.graph.CircularDependencyError
Начало » Django » [Решено] Не могу произвести migrate - django.db.migrations.graph.CircularDependencyError
1
TitanFighter
Всех приветствую.
Помогите пожалуйста.

Есть 2 аппликухи

# app_places
from django.db import models
class Place(models.Model):
    movie = models.ManyToManyField('app_shows_and_times.Show', through='app_shows_and_times.Showtime')
    place_name = models.CharField(max_length=50, db_index=True)
    place_street = models.CharField('Street', max_length=80)
    place_phone = models.CharField('Phone', max_length=60)
    class Meta:
        db_table = 'place'

# app_shows_and_times
from django.db import models
        
class Show(models.Model):
    show_name = models.CharField(max_length=100, db_index=True)
    class Meta:
        db_table = 'show'
class Showtime(models.Model):
    showtime_dates = models.CharField('Date', max_length=30)
    showtime_times = models.CharField('Time', max_length=30)
    showtime_place = models.ForeignKey('app_places.Place', verbose_name='Place')
    showtime_show = models.ForeignKey(Show, verbose_name='Show name')
    class Meta:
        db_table = 'showtime'

makemigrations каждой из них проходит на ОК. Делаю migrate и выдает вот такое:
Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/antonio/projects/my_site/lib/python3.4/site-packages/django/core/management/__init__.py", line 338, in execute_from_command_line
    utility.execute()
  File "/home/antonio/projects/my_site/lib/python3.4/site-packages/django/core/management/__init__.py", line 330, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/antonio/projects/my_site/lib/python3.4/site-packages/django/core/management/base.py", line 393, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/antonio/projects/my_site/lib/python3.4/site-packages/django/core/management/base.py", line 444, in execute
    output = self.handle(*args, **options)
  File "/home/antonio/projects/my_site/lib/python3.4/site-packages/django/core/management/commands/migrate.py", line 146, in handle
    plan = executor.migration_plan(targets)
  File "/home/antonio/projects/my_site/lib/python3.4/site-packages/django/db/migrations/executor.py", line 59, in migration_plan
    for migration in self.loader.graph.forwards_plan(target):
  File "/home/antonio/projects/my_site/lib/python3.4/site-packages/django/db/migrations/graph.py", line 139, in forwards_plan
    self.ensure_not_cyclic(node, lambda x: (parent.key for parent in self.node_map[x].parents))
  File "/home/antonio/projects/my_site/lib/python3.4/site-packages/django/db/migrations/graph.py", line 196, in ensure_not_cyclic
    raise CircularDependencyError(", ".join("%s.%s" % n for n in cycle))
django.db.migrations.graph.CircularDependencyError: app_shows_and_times.0001_initial, app_places.0001_initial

Если обе модели сливаю в один файл, migrate проходит на ура. Но я не хочу сливать эти 2 аппликухи в один файл.

Если закомментировать
movie = models.ManyToManyField('app_shows_and_times.Show', through='app_shows_and_times.Showtime')
тоже все проходит замечательно. Вроде все сделал по букварю.

Благодарю.
________________________________________________________________
РЕШЕНИЕ
1. Комментирую
movie = models.ManyToManyField('app_shows_and_times.Show', through='app_shows_and_times.Showtime')
2. makemigrations app_shows_and_times -> ok
3. makemigrations app_places -> ok
4. Снимаю комментарий с кода в п.1
5. еще раз makemigrations app_places -> ok
6. migrate -> ok!
FishHook
У вас уже есть M2M связь таблиц Place и ShowTime, вы делаете еще одну связь
showtime_show = models.ForeignKey(Show, verbose_name='Show name')
Зачем? Скорее всего вы плохо продумали схему данных, но если вам все-таки нужны такие странные связи, то попробуйте задавать related_name чтобы джанга смогла разобраться какую именно связь вы имеете ввиду, когда будете писать запросы.
TitanFighter
Добрый день FishHook

Мои странные связи сделаны исходя из документации
+ в данной теме в посте №7 товарищ PooH сказал, что все верно (с поправкой на то, что он подзабыл Джангу).

Я что-то недопонимаю?
FishHook
TitanFighter
Я что-то недопонимаю?
Мне так кажется.
У вас модель Place связана с моделью Showtime два раза
раз
movie = models.ManyToManyField('app_shows_and_times.Show', through='app_shows_and_times.Showtime')
два
showtime_place = models.ForeignKey('app_places.Place', verbose_name='Place')

зачем?
TitanFighter
Логически я понимаю о чем вы говорите, но в документации почему то указано это повторение:

from django.db import models
class Person(models.Model):
    name = models.CharField(max_length=128)
    def __str__(self):              # __unicode__ on Python 2
        return self.name
class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')
    def __str__(self):              # __unicode__ on Python 2
        return self.name
class Membership(models.Model):
    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

раз
members = models.ManyToManyField(Person, through='Membership')

два
group = models.ForeignKey(Group)
Зачем?

Я ведь делаю по документации за тем исключением, что в документации используются все модели в одном приложении и все работает, а у меня в двух приложениях и эррорит. НО если я все модели переношу в одно приложение (как в документации), все начинает работать, но это 1) меня не устраивает 2) я хочу разобраться в чем дело.

В документации зря усложнили код и можно открывать тикет?
FishHook
Стоп, я сразу то не увидел, что одна из моделей у вас это through для М2М.
Вышесказанное отменяется.
Вероятно, это ограничение джанговского ОРМа, through модель должна быть описана в том же приложении, где она используется. Вам не надо все модели описывать в одном приложении, а только те которые вы используете в качестве промежуточных для привязки многие-ко-многим (в through).
nnmware
С чем-то подобным сталкивался, навскидку может неточно скажу, но у меня работало и сработало.
Конечно это ограничение джанги, она пробует создавать модели, там ссылка на другую еще несозданную модель, все логично.
Если базы пустые и создаются с нуля, то
1) убрать в одной модели
showtime_place = models.ForeignKey('app_places.Place', verbose_name='Place')
2) Сделать makemigrations
3) migrate app_shows_and_times
4) migrate app_places
5) вернуть убранное поле
6) makemigrations и migrate app_shows_and_times

Возможно есть более изящное решение, но в доке джанго примерно такая же рекомендация
Там они еще предлагают потом squashmigrations сделать, чтобы свернуть миграции в одну, я не делал.
Полезно читнуть

А если данные в моделях уже есть, то там надо в сторону fakemigrations смотреть, увы точно не подскажу.
FishHook
А может они просто не в той последовательности импортируются?
Логично было бы, если бы первой импортировалась модель Place, а потом Showtime.
TitanFighter, расставьте принты в классах, как оно у вас происходит?
TitanFighter
nnmware
1) убрать в одной модели
showtime_place = models.ForeignKey('app_places.Place', verbose_name='Place')
Уже на этом шаге всплывает
SystemCheckError: System check identified some issues:
ERRORS:
app_shows_and_times.Showtime: (fields.E336) The model is used as an intermediate model by 'app_places.Place.movie', but it does not have a foreign key to 'Place' or 'Show'.
Если убирать
movie = models.ManyToManyField('app_shows_and_times.Show', through='app_shows_and_times.Showtime')
вместо предложенной вами строки,
то по итогу просто не создается еще одна таблица (“спаренная”), хотя migrate заканчивается успешно. (Чуть ниже указана последовательность действий в “Путь №1”)

FishHook
TitanFighter, расставьте принты в классах, как оно у вас происходит?

#settings
PROJECT_APPS = [
    'app_places',
    'app_shows_and_times',
]
#принты
Place
Show
Showtime

#settings
PROJECT_APPS = [
    'app_shows_and_times',
    'app_places',
]
#принты
Show
Showtime
Place
Ошибка на команду migrate после изменения позиций аппликух в файле settings не изменилась.
____
Я искусственно сымитировал случай, когда один класс импортируется раньше другого.
Получил такую ошибку
NameError: name 'Show' is not defined
выполняя makemigrations app_shows_and_times или makemigrations app_places

Можно сделать вывод, что при makemigrations сначала идет импортирование моделей, если импортирование проходит успешно, тогда создаются миграции.
Видимо моя проблема не с импортом связана, а с миграцией.

Едит1:
Погуглил еще немного, сопоставив найденное инфо с предложенными шагами от nnmware получил некий незначительный прогресс.

Что попробовал:
Путь №1
1. Комментирую
movie = models.ManyToManyField('app_shows_and_times.Show', through='app_shows_and_times.Showtime')
2. makemigrations app_shows_and_times -> ok
3. makemigrations app_places -> ok
4. Снимаю комментарий с кода в п.1
5. еще раз makemigrations app_places -> ok
6. migrate -> ok! но млин падла НЕ СОЗДАЕТ мне сводную таблицу place_movie (в которой связываются ключи place_id и show_id)
Едит2:
7. Ок, таблица не создалась. Для эксперимента из п.1 оставляю только movie = models.ManyToManyField('app_shows_and_times.Show') и убираю through='app_shows_and_times.Showtime'
8. makemigrations app_places -> ok
9. migrate заканчивается ожидаемым
ValueError: Cannot alter field app_places.Place.movie into app_places.Place.movie - they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)
Вопрос этого Едита №2 - Таблицы нету, поля нового тоже никакого нету (я просмотрел), на что же тогда ругань Джанги?
С поправкой на ночь и тугодумство пришел светлый вопрос - может в данном случае и не должно быть никакой новой таблицы, так как вместо сводной таблицы у нас ключи place_id и show_id указываются в полях showtime_place и showtime_show
class Showtime(models.Model):
    #####
    showtime_place = models.ForeignKey('app_places.Place', verbose_name='Place')
    showtime_show = models.ForeignKey(Show, verbose_name='Show name')
???

Путь №2
1. Комментирую
movie = models.ManyToManyField('app_shows_and_times.Show', through='app_shows_and_times.Showtime')
2. makemigrations app_shows_and_times -> ok
3. makemigrations app_places -> ok
4. Снимаю комментарий с кода в п.1 и УДАЛЯЮ!!! through='app_shows_and_times.Showtime', т.е делаю
movie = models.ManyToManyField('app_shows_and_times.Show')
5. еще раз makemigrations app_places -> ok
6. migrate -> ok! ПОЛУЧАЮ!!! недостающую сводную таблицу place_movie (в которой связываются ключи place_id и show_id)
______
Но мы хотим с дополнительными полями!
7. Добавляю удаленную часть строки through='app_shows_and_times.Showtime
8. makemigrations app_places -> ok
9. migrate заканчивается ожидаемым
ValueError: Cannot alter field app_places.Place.movie into app_places.Place.movie - they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)


На данный момент мне видится 4 варианта:
1) Понять, почему не создается таблица place_movie из “Путь №1”.
Не имею достаточных знаний, чтобы найти причину самостоятельно. Прошу тут помощи.
Похож ли этот случай на баг? Может постучаться к девам\создать тикет?
В соответствии с Едит2 описанным выше - может и не должно быть никакой отдельной таблицы при использовании параметра through в поле many2many?

2) Заставить как-то Django добавить к полю many2many параметр through, чтоб не возникала ошибка из “Путь №2”.
Гуглил. Предложили создать еще одно поле many2many, добавить в него параметр through, а первое закомментировать. Вообщем, не получится в моем случае - упремся опять в CircularDependencyError

3) Может кто-то еще что-то предложит?

4) Я нагуглил, что с django.db.migrations.graph.CircularDependencyError были проблемы у разных людей в разное время на разных версиях Джанго, и девы фиксили их. Сейчас все работает из коробки в варианте, когда параметр through в many2many ссылается на модель в этом же приложении и нету никакий проблем с миграцией.
По идее, оно должно работать тогда и при ссылке на модель в другом приложении (т.е. без костылей в виде комментирования строки с полем many2many и параметром through + игранием в makemigrations)?
Похож ли этот случай на баг? Может постучаться к девам\создать тикет?

Едит3:
Касательно вопроса заданного в Едит2 по поводу “должна ли вообще создаваться таблица”, пришло озарение, как наверняка можно проверить - взять из документации Джанги код и запустить у себя. При параметре through доп таблица не создается, а используется существующая (которая задается этим самым параметром through, что является логичным, если проследить связи в модели Place “many-2-many+throught” и в модели Showtime, где есть “2 шт ForeignKey”).

Последний вопрос, перед тем как “закрыть” тему:
4) Я нагуглил, что с django.db.migrations.graph.CircularDependencyError были проблемы у разных людей в разное время на разных версиях Джанго, и девы фиксили их. Сейчас все работает из коробки в варианте, когда параметр through в many2many ссылается на модель в этом же приложении и нету никакий проблем с миграцией.
По идее, оно должно работать тогда и при ссылке на модель в другом приложении (т.е. без костылей в виде комментирования строки с полем many2many и параметром through + игранием в makemigrations)?
Похож ли этот случай на баг? Может постучаться к девам\создать тикет?
nnmware
Я тут больше ничего подсказать не могу, throught вообще не использовал никогда, не было надобности.
Может и бага, но как ее грамотно описать- хз.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB