Миграция Django с нуля не удалась: 'no such table', после добавления приемников сигналов
Я добавил в свой код несколько приемников сигналов, и все работало нормально, пока я не переместил его в систему контроля версий, и конвейер CI/CD не сработал. При попытке миграции он выдавал сообщение:
django.db.utils.OperationalError: no such table: badges_badge
Но миграции работали на моей машине!
Однако CI/CD начинается с нуля, поэтому я попробовал удалить мои db.sqlite3
локально, затем попытался повторно мигрировать с python manage.py migrate
:
django.db.utils.OperationalError: no such table: badges_badge
Так что миграция из существующей базы данных работает, но не из новой.
Мой signals.py
:
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from badges.badge_stuff import badges
@receiver(post_save)
def update_badges(sender, **kwargs):
for badge in badges:
badge.update()
Мой apps.py
:
from django.apps import AppConfig
class BadgesConfig(AppConfig):
name = 'badges'
def ready(self):
# Register signal listeners.
from . import signals
Почему это работает с существующей базой данных, но не работает при ее инициализации? Как это исправить?
У меня была такая проблема, и она была связана с тем, как я регистрировал свои сигналы.
Проблема в том, что код в apps.ready()
запускается до любых миграций, поэтому если signals.py
зависит от моделей, которые еще не существуют в базе данных (например, при миграции с нуля), это приведет к ошибке. Это может произойти, когда ваш signals.py
импортирует другие модули, которые затем зависят от ваших моделей.
Здесь badges.badge_stuff.badges
импортирует модель Badge
, которая создается при первой миграции. Поэтому он не может найти ее.
Чтобы исправить это, мы можем использовать post_migrate
сигнал для регистрации всех наших сигналов после любых миграций, так что все необходимые модели будут созданы до запуска кода сигнала.
Модифицируйте вышеуказанное следующим образом:
from django.apps import AppConfig
from django.core.signals import pre_save, post_migrate
class BadgesConfig(AppConfig):
name = 'badges'
def register_signals(self, **kwargs):
# If your signals are decorated with @receiver, the line below is all you need
from . import signals
# Otherwise, explicitly connect the signal handler
pre_save.connect(signals.my_callback)
def ready(self):
post_migrate.connect(self.register_signals, sender=self)
И, надеюсь, теперь ваши миграции будут работать!
Не забудьте зарегистрировать ваше приложение в INSTALLED_APPS
, используя этот новый AppConfig в settings.py
:
INSTALLED_APPS = [
...
'badges.apps.BadgesConfig',
# Or just 'badges' for Django 4.0+
...
]