S
S
Sazoks2021-12-25 14:51:53
Django
Sazoks, 2021-12-25 14:51:53

Django ORM | Error in applying SingletonModel?

There is a need to create dynamic site settings for storing phone numbers, addresses, social networks and other things. The data needs to be stored in a database, we work with it through ORM. Therefore, a model is needed. The model to store these settings. It is also obvious that there should be only one entry in this table. Those. our model must implement the Singleton pattern.

Below is my solution with explanations.

An abstract SingletonModel. It is implemented in a regular python package, i.e. NOT in the app.

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()


Site settings model:
class SiteSettings(SingletonModel):
    ...

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

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


Registering the site settings model in the admin panel:
@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


The problem is that when I run the migration I get the error
django.db.utils.OperationalError: no such table: common_info_sitesettings


As I understand it, somewhere there is an attempt to access the common_info_sitesettings table at a time when this table does not yet exist. Honestly, I don't understand what the problem is. I tried to move the SingletonModel implementation to a separate application that I registered in the project. Tried to move SingletonModel into one module with SiteSettings. It didn't work either. In general, I do not understand what is wrong. But I still managed to run the migrations, but not in the way I need.

When in the model registration class (in SiteSettingsAdmin) in the initializer I replaced this
...
        # Создаем дефолтный экземпляр настроек при
        # первом запросе к странице с настройками.
        try:
            SiteSettings.load().save()
        except ProgrammingError:
            pass

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

I started all the migrations, but there was no access to creating site settings in the admin panel (because I disabled this feature). In theory, at the first start, an empty instance of the SiteSettings model should have been created, because. this logic is inherited by SiteSettings from SingletonModel here:
@classmethod
    def load(cls):
        """Метод загрузки единственно экземпляра модели"""

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

However, this does not happen.

In general, I ask you to help you understand this problem, or else show you how to properly implement Singleton for models.

Thank you!

Answer the question

In order to leave comments, you need to log in

2 answer(s)
S
Sazoks, 2021-12-28
@Sazoks

The solution turned out to be very simple and obvious.
The error was that during the creation of migrations, the object loading code was executed before the table was created in the database.
You just need to create a table in the database before migrations, and in order not to mess around with this, you can simply comment out the registration class of the model, run the migrations (then the table will be created fully configured) and then uncomment the registration class.

V
Vladimir Kuts, 2021-12-27
@fox_12

Here is a good implementation example:
Singleton Design Pattern Example
In your example, for example, it is not clear why to do this:
class Meta(SingletonModel.Meta):
You then inherit abstract = True for the model. But your specific model is not abstract ...
why such a construction:

self.__class__.objects.exclude(pk=self.id).delete()

if in theory you still have only one record in the table...

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question