python

Расширяем и используем Flatpages в Django. Встраиваем CKEditor

  • понедельник, 23 октября 2017 г. в 03:12:05
https://habrahabr.ru/post/340686/
  • Python
  • Django


Здравствуйте, сегодня я хотел бы вам рассказать о том, как сделать модель, которая хранит в себе обычные страницы, а не отдельные записи в базе данных (для ListView, TemplateView и тд). Речь пойдёт о том, как расширить и дополнить существующие в Django flatpages. Но хотелось бы рассказать о проблеме, с которой я столкнулся и почему решил поделиться данным функционалом. Часто возникает ситуация, когда в админке для администратора сайта нужно реализовать функционал самой обычной страницы (одна запись в БД – это одна страница, где прописывается url, контент и доп. инфа для конкретной страницы). Тем самым можно создавать прямо из админки новые страницы с любым url и контентом.

Приведу пример: была поставлена задача реализовать такую страницу, где было бы обычное текстовое поле, к которому прикручен ckeditor и администратор мог бы менять и писать нужный текст о компании, а также переписывать его. Первая мысль, которая тебя посещает — это обычная запись в модели и в контроллере (вьюхе) сделать класс на основе TemplateView, и страница создана без проблем. Но далее начинаешь понимать, что это очень плохой стиль и если этот администратор начнёт добавлять новые записи в бд, то вёрстка поедет. Сразу же приходит в голову способ переопределить, например, метод get_context_data или get_queryset (в ListView). И там сделать нужную выборку (например, брать только самую первую запись из БД) где собственно администратор и будет править нужную страницу, но всё равно у него остаётся возможность добавлять новые записи в бд, которые просто будут игнорироваться. Придумав ещё пару способов, я отбросил эту затею. Посчитав это плохим тоном, я вспомнил о существование flatpages в Django, но освежив в памяти их функционал, стало ясно, что добавить что-то в их контекст данных невозможно, но можно, например, указать нужный шаблон, который обрабатывается ‘шаблонизатором’, но вот текст, который вы задаёте в поле текста, не обрабатывается ‘шаблонизатором’. Это стоит учитывать, но для меня это не являлось большой проблемой. Но вот что являлось, так это то что это изначально flatpages не расширяемый модуль и прикрутить столь важный ckeditor невозможно. Пришлось думать, как это можно реализовать. Но именно этот функционал мне и нужен был. После вводной части теперь давайте перейдём к более подробному изучению столь хорошему модулю flatpages, но к сожалению, неполноценному, давайте это исправим.

Что такое статичные страницы в джанге – это страницы содержимое которых не генерируется на основе хранящихся в модели данных, а задаётся в виде обычного html кода в соответствующих, заранее заданных, полях.

Основные недостатки:

  1. Данные не генерируются на основе данных из модели, а следовательно во views.py (далее буду называть контроллером по MVC, а не представление по MVT) мы не можем как то их обработать, дополнить и поместить в контекст данных что либо ещё. А также не можем изначально расширить или изменить модель (models.py).
  2. Содержимое таких страниц должно представлять собой чисты html код — это главная причина почему из коробки flatpages неполноценны. Так же стоит понимать, что данный код не обрабатывается ‘шаблонизатором’

Основные плюсы:

  1. Cодержимое flatpages включает в себя интернет-адрес страницы, её заголовок, содержимое и самое главное путь к файлу шаблона. Последний пункт, является ключевым, не смотря на то, что мы не можем передавать данные, но можем сделать нужную страницу используя: базовый шаблон, в котором у нас есть меню настроенное с помощью тегов и переменных джанги, задать места где нужно выводить данные из flatpages, подключать ‘включённые шаблоны’ и тд.
  2. Одна запись в модели соответствует одной странице на сайте, что является большим плюсом, система полностью настроена нужным образом, что позволяет не писать нам собственный велосипед.

Настройка проекта:


  1. В settings.py добавляем.

    INSTALLED_APPS = [
    …
        'django.contrib.sites', '''Служит для обеспечения работы нескольких web-сайтов на одной копии Django. В данном проекте, это нужно для корректной работы flatpages.'''
        'django.contrib.flatpages',
        'flatpage_main' #Потребуется в дальнейшем, имя можете задать любое.
    …
    ]
  2. Добавляем в проект(settings.py) SITE_ID = 1 и в MIDDLEWARE 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
  3. Делаем синхронизацию с базой данных для добавление нужных таблиц в БД (до 5 шага 'flatpage_main' не писать в INSTALLED_APPS) .
  4. Заходим на административный сайт проверяем, что всё работает. У вас должна появиться графа ‘простые страницы’.
  5. Создаём новое приложение — python manage.py startapp 'flatpage_main'.
  6. Настраиваем ckeditor, на просторах интернета этот материал уже есть, думаю вы сможете его поставить и настроить, а использовать научитесь на этом примере. Если будет нужно, постараюсь написать статейку на хабр, но материал по этому вопросу есть в интернете.

Реализация поставленной задачи:


  1. Зайдём в модели и добавим следующий код —

    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, расширяя её.
    А далее добавляем любые нужные нам поля, которые хотим, чтобы были в админке и в будущем на самой странице, тем самым можем добавить поле для любого нужного нам контента.
  2. Следующая ступень – это 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), лишние настройки были убраны.

    Последние две строчки — это снятие и регистрация моделей в админке.
  3. Третий шаг это — urls.py.

     url(r'^main/$', views.flatpage, {'url': '/main/'}, name = 'main'),

    Добавляем эту строчку если чётко знаем что данная страница точно будет, и мы хотим обрабатывать её в базовом шаблоне, например, в меню, и ссылаться на неё по имени в переменной шаблона, потому что сам шаблон рендерится ‘шаблонизатором’.
    Для других страниц вы можете задать —
    url(r'^/', include('django.contrib.flatpages.urls')),

    Тем самым любой другой url адрес будет привязан к url адресу, который вы зададите при создание новой страницы.
  4. Четвёртый шаг это – 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.
  5. Деплойт – при деплойте вы столкнётесь с парой ошибок связанных с работой ‘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 и разобрались как его настраивать. Разобрали все попутные моменты и сложности. А самое главное соединили это всё в удобный и полезный инструмент, который позволит грамотно администрировать сайт в дальнейшем, а также развивать его, что конечно же удобно для конечного пользователя. Надеюсь данная статья была полезна и многим она поможет в работе! Кому-то сэкономит время. А кто-то напишет в комментариях ниже дополнительные советы и дополнит её!