Допустим, вы создаете сайт с CMS на базе Django, который должен иметь какие-то динамические настройки сайта, которые будут доступны пользователю. Например, название сайта, какая-то специализированная информация, при этом вы учитываете возможность мультиязычности. Что тогда можно использовать для этого? У меня возникла идея использовать базу данных.
Для реализации этого требуется следующее:
- Создание модели данных, которая всегда будет содержать только один объект, то есть только одну запись. То есть это будет Singleton Model.
- Запретить удаление этой записи и создание новых в админке Django
- Возможность использовать информацию из этой модели прямо в шаблоне, без загрузки настроек сайта в функцию просмотра.
Разберемся по порядку, как это реализовать.
SingletonModel
Я нашел на GitHub код абстрактной SingletonModel, написанной еще в 2012 году. Вот ссылка на gist .
Вот код для этой SingletonModel
class SingletonModel(models.Model): class Meta: abstract = True def save(self, *args, **kwargs): self.__class__.objects.exclude(id=self.id).delete() super(SingletonModel, self).save(*args, **kwargs) @classmethod def load(cls): try: return cls.objects.get() except cls.DoesNotExist: return cls()
Модель в методе save автоматически сохраняет все остальные при сохранении объекта, что позволяет всегда сохранять в базе только один экземпляр этой модели.
Метод load берет из БД единственный объект настроек, если объекта в БД нет, возвращает новый экземпляр этой модели, который нужно будет потом сохранить.
Давайте создадим собственный класс настроек, чтобы использовать его для наших нужд в будущем.
# -*- coding: utf-8 -*- from django.db import models from django.utils.translation import ugettext_lazy as _ from my_app.singleton_model import SingletonModel class SiteSettings(SingletonModel): site_url = models.URLField(verbose_name=_('Website url'), max_length=256) title = models.CharField(verbose_name=_('Title'), max_length=256) def __str__(self): return 'Configuration'
Настройка панели администратора
Теперь нужно запретить удаление этой записи и создание новых в админке Django.
# -*- coding: utf-8 -*- from django.contrib import admin from django.db.utils import ProgrammingError from my_app.models import SiteSettings class SiteSettingsAdmin(admin.ModelAdmin): # Create a default object on the first page of SiteSettingsAdmin with a list of settings def __init__(self, model, admin_site): super().__init__(model, admin_site) # be sure to wrap the loading and saving SiteSettings in a try catch, # so that you can create database migrations try: SiteSettings.load().save() except ProgrammingError: pass # prohibit adding new settings def has_add_permission(self, request, obj=None): return False # as well as deleting existing def has_delete_permission(self, request, obj=None): return False admin.site.register(SiteSettings, SiteSettingsAdmin)
Теперь настройки можно использовать в коде Python.
Создание контекстного процессора для загрузки настроек в шаблон
context_proccessors.py
# -*- coding: utf-8 -*- from evileg_settings.models import SiteSettings def load_settings(request): return {'site_settings': SiteSettings.load()}
settings.py
Затем подключаем load_settings
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', # Загружаем наши настройки при каждом рендеринге шаблона с контекстом 'my_app.context_processors.load_settings', ], }, }, ]
Использование в шаблоне
<h1>{{ site_settings.title }}</h1>
Вывод
Вот так быстро и просто можно реализовать простые динамические настройки сайта в Django и потом привязать их ко всем интересующим нас параметрам сайта.
Преимуществом такого подхода будет возможность использовать внешние ключи для некоторых специальных данных на сайте, а также использование django modeltranslation, а значит проблем с мультиязычностью не будет.