Есть необходимость создать динамические настройки сайта для хранения телефонов, адресов, социальных сетей и прочего. Данные нужно хранить в БД, работаем мы с ней через 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 для моделей.
Спасибо!