https://habrahabr.ru/post/340686/Здравствуйте, сегодня я хотел бы вам рассказать о том, как сделать модель, которая хранит в себе обычные страницы, а не отдельные записи в базе данных (для ListView, TemplateView и тд). Речь пойдёт о том, как расширить и дополнить существующие в Django flatpages. Но хотелось бы рассказать о проблеме, с которой я столкнулся и почему решил поделиться данным функционалом. Часто возникает ситуация, когда в админке для администратора сайта нужно реализовать функционал самой обычной страницы (одна запись в БД – это одна страница, где прописывается url, контент и доп. инфа для конкретной страницы). Тем самым можно создавать прямо из админки новые страницы с любым url и контентом.
Приведу пример: была поставлена задача реализовать такую страницу, где было бы обычное текстовое поле, к которому прикручен ckeditor и администратор мог бы менять и писать нужный текст о компании, а также переписывать его. Первая мысль, которая тебя посещает — это обычная запись в модели и в контроллере (вьюхе) сделать класс на основе TemplateView, и страница создана без проблем. Но далее начинаешь понимать, что это очень плохой стиль и если этот администратор начнёт добавлять новые записи в бд, то вёрстка поедет. Сразу же приходит в голову способ переопределить, например, метод get_context_data или get_queryset (в ListView). И там сделать нужную выборку (например, брать только самую первую запись из БД) где собственно администратор и будет править нужную страницу, но всё равно у него остаётся возможность добавлять новые записи в бд, которые просто будут игнорироваться. Придумав ещё пару способов, я отбросил эту затею. Посчитав это плохим тоном, я вспомнил о существование flatpages в Django, но освежив в памяти их функционал, стало ясно, что добавить что-то в их контекст данных невозможно, но можно, например, указать нужный шаблон, который обрабатывается ‘шаблонизатором’, но вот текст, который вы задаёте в поле текста, не обрабатывается ‘шаблонизатором’. Это стоит учитывать, но для меня это не являлось большой проблемой. Но вот что являлось, так это то что это изначально flatpages не расширяемый модуль и прикрутить столь важный ckeditor невозможно. Пришлось думать, как это можно реализовать. Но именно этот функционал мне и нужен был. После вводной части теперь давайте перейдём к более подробному изучению столь хорошему модулю flatpages, но к сожалению, неполноценному, давайте это исправим.
Что такое статичные страницы в джанге – это страницы содержимое которых не генерируется на основе хранящихся в модели данных, а задаётся в виде обычного html кода в соответствующих, заранее заданных, полях.
Основные недостатки:- Данные не генерируются на основе данных из модели, а следовательно во views.py (далее буду называть контроллером по MVC, а не представление по MVT) мы не можем как то их обработать, дополнить и поместить в контекст данных что либо ещё. А также не можем изначально расширить или изменить модель (models.py).
- Содержимое таких страниц должно представлять собой чисты html код — это главная причина почему из коробки flatpages неполноценны. Так же стоит понимать, что данный код не обрабатывается ‘шаблонизатором’
Основные плюсы:- Cодержимое flatpages включает в себя интернет-адрес страницы, её заголовок, содержимое и самое главное путь к файлу шаблона. Последний пункт, является ключевым, не смотря на то, что мы не можем передавать данные, но можем сделать нужную страницу используя: базовый шаблон, в котором у нас есть меню настроенное с помощью тегов и переменных джанги, задать места где нужно выводить данные из flatpages, подключать ‘включённые шаблоны’ и тд.
- Одна запись в модели соответствует одной странице на сайте, что является большим плюсом, система полностью настроена нужным образом, что позволяет не писать нам собственный велосипед.
Настройка проекта:
- В settings.py добавляем.
INSTALLED_APPS = [
…
'django.contrib.sites', '''Служит для обеспечения работы нескольких web-сайтов на одной копии Django. В данном проекте, это нужно для корректной работы flatpages.'''
'django.contrib.flatpages',
'flatpage_main' #Потребуется в дальнейшем, имя можете задать любое.
…
]
- Добавляем в проект(settings.py) SITE_ID = 1 и в MIDDLEWARE 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
- Делаем синхронизацию с базой данных для добавление нужных таблиц в БД (до 5 шага 'flatpage_main' не писать в INSTALLED_APPS) .
- Заходим на административный сайт проверяем, что всё работает. У вас должна появиться графа ‘простые страницы’.
- Создаём новое приложение — python manage.py startapp 'flatpage_main'.
- Настраиваем ckeditor, на просторах интернета этот материал уже есть, думаю вы сможете его поставить и настроить, а использовать научитесь на этом примере. Если будет нужно, постараюсь написать статейку на хабр, но материал по этому вопросу есть в интернете.
Реализация поставленной задачи:
- Зайдём в модели и добавим следующий код —
from django.db import models
from django.contrib.flatpages.models import FlatPage
from ckeditor_uploader.fields import RichTextUploadingField
class NewFlatpage(models.Model):
flatpage = models.OneToOneField(FlatPage)
description = RichTextUploadingField(verbose_name = 'Основной текстовый контент страницы',default='')
text_block = RichTextUploadingField(verbose_name='Дополнительный блок текста',default='')
def __str__(self):
return self.flatpage.title
class Meta:
verbose_name = "Содержание страницы"
verbose_name_plural = "Содержание страницы"
Разберёмся что тут написано:
… import FlatPage Добавляем модель, на которую будем ссылаться.
… import RichTextUploadingField спец поле, которое нужно для работы ckeditor
В нашем новом классе ссылаемся через OneToOneField на модель FlatPage, тем самым создаём нужную связь, что позволяет нам увеличить базовую функциональность flatpages, расширяя её.
А далее добавляем любые нужные нам поля, которые хотим, чтобы были в админке и в будущем на самой странице, тем самым можем добавить поле для любого нужного нам контента.
- Следующая ступень – это admin.py.
from django.contrib import admin
from django.contrib.flatpages.admin import FlatPageAdmin
from .models import *
class NewFlatpageInline(admin.StackedInline):
model = NewFlatpage
verbose_name = "Содержание"
class FlatPageNewAdmin(FlatPageAdmin):
inlines = [NewFlatpageInline]
fieldsets = (
(None, {'fields': ('url', 'title', 'sites')}),
(('Advanced options'), {
'fields': ('template_name',),
}),
)
list_display = ('url', 'title')
list_filter = ('sites', 'registration_required')
search_fields = ('url', 'title')
admin.site.unregister(FlatPage)
admin.site.register(FlatPage, FlatPageNewAdmin)
Приступим к разбору:
Первый класс NewFlatpageInline и атрибут во втором классе inlines = [NewFlatpageInline], создаёт связь между этими классами, давая возможность на одной странице выводить поля из двух взаимосвязанных таблиц (‘позволяет редактировать связанные объекты на одной странице с родительским объектом’).
fieldsets позволяет задать поля и настройки которые будут выведены в интерфейсе администратора у родительского объекта(flatpage), лишние настройки были убраны.
Последние две строчки — это снятие и регистрация моделей в админке.
- Третий шаг это — urls.py.
url(r'^main/$', views.flatpage, {'url': '/main/'}, name = 'main'),
Добавляем эту строчку если чётко знаем что данная страница точно будет, и мы хотим обрабатывать её в базовом шаблоне, например, в меню, и ссылаться на неё по имени в переменной шаблона, потому что сам шаблон рендерится ‘шаблонизатором’.
Для других страниц вы можете задать — url(r'^/', include('django.contrib.flatpages.urls')),
Тем самым любой другой url адрес будет привязан к url адресу, который вы зададите при создание новой страницы.
- Четвёртый шаг это – template:
{% url 'main' as main %}
{% if request.path == main %}
<li class="navigation__elem navigation__elem--main navigation__elem--current">
<a href="{% url 'main'%}" class="navigation__href navigation__href--current"><i class="demo-icon icon-main__icon"></i>Главная</a></li>
{% else %}
<li class="navigation__elem nav-active"><a href="{% url 'main'%}" class="navigation__href"><i class="demo-icon icon-main__icon"></i>Главная</a></li>
{% endif %}
Выше написал пример работы меню с flatpages и остальными страницами основанных на контроллерах и шаблонах, думаю код тут предельно ясный и понятный, комментарии излишни. Как видим получить имя url адреса из urls.py, а потом обработать его не составляет никакого труда, тем самым закрывая последнюю сложность в реализации.
Последний штрих, в шаблоне, который мы указали при создании страницы, при создании страницы в админке будет поле в которому указывается шаблон для этой страницы (напр: main.html). В этом шаблоне добавляем нужные нам поля, используя переменные
{{flatpage.newflatpage.description|safe}}
и
{{flatpage.newflatpage.text_block|safe}}
, тем самым позволяя нам вывести нужные данные на итоговую страницу. В этом шаблоне работает всё тоже самое что и в других, например, наследование от base.html.
- Деплойт – при деплойте вы столкнётесь с парой ошибок связанных с работой ‘django.contrib.sites', вы должны войти в шелл (python manage.py shell) и написать следующие команды:
>>> from django.contrib.sites.models import Site
>>> site = Site.objects.create(domain='http://ваш_домен.ru/', name=''http://ваш_домен.ru/)
>>> site.save()
Это должно помочь решить проблему с эксепшн.
На этом всё! Мы получили крутой и удобный способ добавлять, редактировать и удалять страницы на своём сайте. Научились реализовывать меню, которое позволит переключаться между активными и не активными пунктами и не важно flatpages это или страницы с контроллерами. Так же поработали с ckeditor и разобрались как его настраивать. Разобрали все попутные моменты и сложности. А самое главное соединили это всё в удобный и полезный инструмент, который позволит грамотно администрировать сайт в дальнейшем, а также развивать его, что конечно же удобно для конечного пользователя. Надеюсь данная статья была полезна и многим она поможет в работе! Кому-то сэкономит время. А кто-то напишет в комментариях ниже дополнительные советы и дополнит её!