Допустим, вы создаете сайт с 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, а значит проблем с мультиязычностью не будет.