Миграции применяются в 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, как и ожидалось.

Вернуться на верх