Миграции применяются в site-packages.django.contrib.sites вместо локального приложения после расширения объекта Site с полем OneToOne
Я позаимствовал идеи из ответа this для расширения объекта Site, созданного по умолчанию во время первоначальной миграции с фреймворком сайтов Django. Новая модель, SiteSettings, устанавливает отношения OneToOne с моделью Site, чтобы добавить дополнительные поля. Затем я использую сигналы для создания объекта SiteSettings.
Когда я выполнил первую миграцию для модели SiteSettings, все вроде бы работало нормально. Был создан объект SiteSettings, который имел связь OneToOne с объектом Site по умолчанию.
Однако, что я не заметил, так это то, что файл миграции не был создан в моем локальном приложении для этого. Я смог сделать миграции и мигрировать просто отлично, поэтому я не уверен, куда делась эта миграция. Он не указан в моей таблице миграций
В общем, поскольку все работало, я не заметил. Затем я продолжил добавлять дополнительные поля в SiteSettings день или два спустя, и заметил, что когда я делал эти миграции, они были для создания модели SiteSettings, а не для обновления ее полей. Тогда я заметил, что первая миграция не была создана в нужном месте. Вторая миграция была создана, однако она была создана в site-packages/django/contrib/sites/migrations/
. Это выглядит следующим образом:
class Migration(migrations.Migration):
dependencies = [
("sites", "0002_alter_domain_unique"), # the initial auto migration
]
operations = [
migrations.CreateModel(
name="SiteSettings",
fields=[
(
"site",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
primary_key=True,
related_name="settings",
serialize=False,
to="sites.site",
verbose_name="site",
),
),
# A bunch of other CharFields that aren't important
],
options={
"verbose_name_plural": "settings",
},
),
]
И мой models.py выглядит следующим образом. Я предполагаю, что проблема может быть в том, что app_label
совпадает с именем django.contrib.sites
, но я не уверен. Я назвал метку так, чтобы она отображалась под сайтами в админке.
class SiteSettings(models.Model):
"""
Extension of the Sites model that holds more info about the site.
"""
site = models.OneToOneField(
Site,
on_delete=models.CASCADE,
primary_key=True,
related_name="settings",
verbose_name="site",
)
# A bunch of other fields that aren't important
def __str__(self):
return self.site.name
class Meta:
app_label = "sites"
verbose_name_plural = "settings"
Вот как выглядит apps.py:
from django.apps import AppConfig
from django.conf import settings
from django.db.models.signals import post_migrate, post_save
def create_default_site_settings(sender, **kwargs):
"""Creates default site settings after migration"""
# App config must be ready for import to work
from django.contrib.sites.models import Site
from .models import SiteSettings
site = Site.objects.get(id=getattr(settings, "SITE_ID", 1))
if not SiteSettings.objects.exists():
SiteSettings.objects.create(site=site)
class CoreConfig(AppConfig):
name = "apps.core"
label = "core"
default_auto_field = "django.db.models.BigAutoField"
def ready(self):
# App config must be ready for import to work
from django.contrib.sites.models import Site
post_migrate.connect(create_default_site_settings, sender=self)
from .signals import create_site_settings
post_save.connect(create_site_settings, sender=Site)
И, наконец, signals.py.
from django.contrib.sites.models import Site
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import SiteSettings
@receiver(post_save, sender=Site)
def create_site_settings(sender, instance, **kwargs):
"""
Creates/updates a SiteSettings object after a Site object.
"""
site_settings, created = SiteSettings.objects.update_or_create(site=instance)
if not created:
site_settings.save()
Является ли это просто проблемой того, что app_label
одинаковы? Я пытаюсь понять, почему так происходит, если это так.
Я выяснил, в чем проблема. Поскольку я запускал свое приложение в контейнере Docker, а app_label
в моей новой модели был установлен тот же app_label
, что и в django.contrib.sites, миграции создавались в path/to/site-packages/django/contrib/sites/migrations/
в контейнере Docker. Не в приложении, скопированном в контейнер, а в фактической установке Django внутри контейнера.
Итак, первоначальная миграция была создана в контейнере, а не локально, чего я сначала не заметил. Когда мой контейнер был перестроен в какой-то момент, файл миграции был стерт, поскольку он заново устанавливает все пакеты Python на новый образ. Когда я пошел делать вторую миграцию, оказалось, что модель так и не была создана, несмотря на то, что таблица существовала в базе данных.
Чтобы исправить это, я вернул app_label
новой модели обратно к значению по умолчанию. Затем мне пришлось пересобрать контейнер, удалить старую таблицу site_sitesettings и удалить "потерянную" запись миграции в таблице django_migrations. Во время следующего цикла миграций была создана новая таблица для core_sitesettings, и я смог получить доступ к моим отношениям OneToOne, как и ожидалось.