Migrations applied in site-packages.django.contrib.sites instead of local app after extending Site object with OneToOne field
Asked Answered
D

1

0

I borrowed ideas from this answer to extend the default Site object created during the initial migration with Django sites framework. The new model, SiteSettings, establishes a OneToOne relationship with the Site model to add additional fields. I then use signals to create the SiteSettings object.

When I made the first migration for the SiteSettings model everything appeared to work fine. A SiteSettings object was created that had a OneToOne relationship with the default Site object.

However, what I didn't notice is that a migration file wasn't created under my local app for this. I was able to makemigrations and migrate just fine, so I'm not sure where that migration went. It's not listed in my migrations table.

Anyway, since it worked I didn't notice. I then proceeded to add additional fields to SiteSettings a day or two later, and noticed when I made those migrations, they were for creating a SiteSettings model, not updating its fields. That's when I noticed that the first migration wasn't created in the right spot. The second migration was created, however it was created in site-packages/django/contrib/sites/migrations/. It looks like this:

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",
        },
    ),
]

And my models.py looks like this. I'm assuming the issue may be with the app_label matching the name of django.contrib.sites, but I'm not sure. The reason I named the label that was so it shows up under sites in the admin.

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"

Here's what the apps.py looks like:

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)

And lastly, 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()

Is this simply an issue with the app_label being the same? I'm trying to wrap my head around why that is if so.

Diplegia answered 28/9, 2022 at 23:13 Comment(2)
Removing the app_label resolves the issue with migrations? Is there an issue with removing it?Ortiz
I was in fact able to remove the custom app_label and get the migrations to apply to the core app instead of contrib.sites. My app was running in a Docker container so I was confused about how to revert the migration files. I had to rebuild the container, delete the old table site_sitesettings and remove the "lost" migration record in django_migrations table. Then a new table was created for core_sitesettings and I can access my OneToOne relationships as expected. I guess there is no easy way to get the settings to appear under the same app_label without messing up migrations.Diplegia
D
0

I figured out the issue. Since I was running my app in a Docker container and had the app_label in my new model set to the same app_label as django.contrib.sites, the migrations were being created in path/to/site-packages/django/contrib/sites/migrations/ in the Docker container. Not in the app copied to the container, but the actual Django install inside the container.

So that initial migration was created in the container and not locally which I didn't notice at first. Once my container was rebuilt at some point, the migration file was wiped since it freshly reinstalls all Python packages onto the new image. When I went to make the second migration, it was like the model was never created despite the table existing in the db.

To fix it, I reverted the app_label of the new model back to its default. Then I had to I had to rebuild the container, delete the old table site_sitesettings and remove the "lost" migration record in django_migrations table. During the next round of migrations a new table was created for core_sitesettings and I could access my OneToOne relationships as expected.

Diplegia answered 29/9, 2022 at 2:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.