@Sazoks

Django ORM | Ошибка в применении SingletonModel?

Есть необходимость создать динамические настройки сайта для хранения телефонов, адресов, социальных сетей и прочего. Данные нужно хранить в БД, работаем мы с ней через ORM. Следовательно, нужна модель. Модель для хранения этих настроек. Также очевидно, что в этой таблице должна быть одна единственная запись. Т.е. наша модель должна реализовывать паттерн Singleton.

Далее представлено мое решение с пояснениями.

Абстрактная модель SingletonModel. Она реализована в обычном python-пакете, т.е. НЕ в приложении.
from django.db import models


class SingletonModel(models.Model):
    """Абстрактный класс синглтон-модели"""

    class Meta:
        """Настройки модели"""
        abstract = True

    def save(self, *args, **kwargs):
        """Метод сохранения записи в БД"""
        self.__class__.objects.exclude(pk=self.id).delete()
        super(SingletonModel, self).save(*args, **kwargs)

    @classmethod
    def load(cls):
        """Метод загрузки единственно экземпляра модели"""

        # Если нет ни одной записи, возвращаем новый экземпляр.
        try:
            return cls.objects.get()
        except cls.DoesNotExist:
            return cls()


Модель настроек сайта:
class SiteSettings(SingletonModel):
    ...

    class Meta(SingletonModel.Meta):
        """Настройки модели"""
        verbose_name = _('Настройки сайта')
        verbose_name_plural = _('Настройки сайта')

    def __str__(self) -> str:
        return _('Настройки')


Регистрация модели настроек сайта в админке:
@admin.register(SiteSettings)
class SiteSettingsAdmin(admin.ModelAdmin):
    ...

    def __init__(self, model, admin_site):
        """Инициализатор класса"""
        super().__init__(model, admin_site)

        # Создаем дефолтный экземпляр настроек при
        # первом запросе к странице с настройками.
        try:
            SiteSettings.load().save()
        except ProgrammingError:
            pass

    def has_add_permission(self, request, obj=None):
        """
        Метод проверки прав на добавление.
        По умолчанию запрещаем добавление новых записей.
        """
        return False

    def has_delete_permission(self, request, obj=None):
        """
        Метод проверки прав на удаление.
        По умолчанию запрещаем удаление записей.
        """
        return False


Проблема в том, что при запуске миграцией я получаю ошибку
django.db.utils.OperationalError: no such table: common_info_sitesettings


Как я понял, где-то происходит попытка доступа к таблице common_info_sitesettings в момент, когда этой таблицы еще нет. Честно, я не понимаю, в чем проблема. Я пробовал вынести реализацию SingletonModel в отдельно приложение, которое регистрировал в проекте. Пробовал перенести SingletonModel в один модуль с SiteSettings. Тоже не сработало. В общем, не понимаю, в чем ошибка. Но мне все же удалось запустить миграции, но не так, как мне нужно.

Когда в классе регистрации модели (в SiteSettingsAdmin) в инициализаторе я заменил это
...
        # Создаем дефолтный экземпляр настроек при
        # первом запросе к странице с настройками.
        try:
            SiteSettings.load().save()
        except ProgrammingError:
            pass

на это
...
    # Создаем дефолтный экземпляр настроек при
    # первом запросе к странице с настройками.
    try:
        SiteSettings.load().save()
    except Exception:
        pass

у меня все миграции запустились, однако в админки не было доступа к созданию настроек сайта (т.к. я отключил эту возможность). По идее, при первом запуске должен был создаться пустой экземпляр модели SiteSettings, т.к. эта логика унаследована SiteSettings от SingletonModel здесь:
@classmethod
    def load(cls):
        """Метод загрузки единственно экземпляра модели"""

        # Если нет ни одной записи, возвращаем новый экземпляр.
        try:
            return cls.objects.get()
        except cls.DoesNotExist:
            return cls()

Однако это не происходит.

В общем, прошу помочь вас разобраться в этой проблеме, либо же показать, как правильно реализовывать Singleton для моделей.

Спасибо!
  • Вопрос задан
  • 258 просмотров
Решения вопроса 1
@Sazoks Автор вопроса
Решение оказалось очень простым и очевидным.
Ошибка заключалась в том, что во время создания миграций код загрузки объекта выполнялся раньше, чем создавалась таблица в БД.
Нужно просто создать таблицу в БД перед миграциями, а чтобы не возиться с этим, можно просто закомментировать класс регистрации модели, запустить миграции (тогда таблица создаться полностью настроенной) и затем раскомментировать класс регистрации.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
fox_12
@fox_12 Куратор тега Django
Расставляю биты, управляю заряженными частицами
Вот неплохой пример реализации:
Singleton Design Pattern Example

В вашем примере например непонятно зачем так делать:
class Meta(SingletonModel.Meta):
Вы ж тогда и abstract = True для модели наследуете. А ведь конкретная модель у вас не абстрактна...

зачем такая конструкция:
self.__class__.objects.exclude(pk=self.id).delete()

если по идее у вас все равно только одна запись в таблице...
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы