Evgenii Legotckoi
Evgenii Legotckoi8 февраля 2022 г. 17:55

Django - Урок 055. Как написать функционал auto populate field

Очень давно хотел написать статью о том, как написать функционал auto populate field для проекта на Django . Это очень полезный функционал, который позволяет изменять содержимое других полей модели в Django при установке значение в то поле, в котором используется auto populate.

Во-первых, зачем это нужно? - Подобный функционал позволяет сократить размеры кода в тех местах, где нужно переписывать другие поля объекта, при их изменении. То есть, вам например не придется каждый раз переопределять метод save, чтобы переписать какое-то поле в случае изменения других полей объекта. Также использование auto populate в принципе более продвинутый и аккуратный способ управления моделями данных в Django.
А также подобный подход моет решить некоторые проблемы и улучшить работу сайта.


Описание подхода на примере markdown полей

Например такой подход я использую на этом сайте для написания контента комментариев и статей, и вообще во всех полях, где используется синтаксис markdown .

Как это работает?

  • Все модели данных имеют два поля content и content_markdown . Поле content_markdown управляет содержимым поля content . То есть в обычной ситуации пользователь не имеет доступа к редактирования содержимого поля content , он всегда редактирует только поле content_markdown .
  • Пользователь, создавая сообщение, редактирует поле content_markdown , которое автоматически генерирут html код из markdown разметки и записывает html в поле content .

Зачем это нужно?

Каждый сам выбирает, для чего ему использовать auto populate. Например, Вы можете автоматически генерировать thumbnail изображение из исходного изображения. Подобный функционал идеально подходит для таких действий.

А конкретно в моём случае получилось так, что я не нашёл подходящего функционала для генерирования html из markdown. Всё что я находил на тот момент, это библиотеки, которые генерировал html в runtime при каждой загрузке страницы с помощью шаблонных тегов. А подобный подход очень сильно снижает производительность сайта. Поэтому я для себя решил, что готов пожертвовать дисковым пространством ради производительности сайта. В конце концов десятикратное увеличение скорости загрузки страницы в некоторых случаях явно предпочтительнее, чем двойной объем занимаемого дискового пространства.

Реализация

Я представлю упрощенный код MarkdownField , который вы могли бы использовать у себя на сайте.

MarkdownField

MarkdownField будет наследоваться от стандартного TextField и будет получать в качестве аргумента имя поля, которое будет использовать для html контента. Это аргумент html_field .

Данное поле имеет метод set_html , который отвечает за генерирование html из markdown разметки, а также установку значения в поле html контента.

Важным моментом является подключение метода set_html к сигналу pre_save в методе contribute_to_class . Более ранний вариант, который я разрабатывал, имел такой недостаток, что при каждом запросе объекта вызывался метод генерирования html. Это была ошибка, которую я впоследствие обнаружил и исправил подобным образом. Теперь контент генерируется только в случае сохранения объекта.

# -*- coding: utf-8 -*-

from django.db import models
from django.db.models.signals import pre_save

from .utils import MarkdownWorker


class MarkdownField(models.TextField):
    """
    This field save markdown text with auto-populate text to html field.
    This field must be used with second text field for html content.
    """

    def set_html(self, instance=None, update_fields=None, **kwargs):
        value = getattr(instance, self.attname)
        if value and len(value) > 0:
            instance.__dict__[self.html_field] = MarkdownWorker(value).get_text()

    def contribute_to_class(self, cls, name, **kwargs):
        super().contribute_to_class(cls, name, **kwargs)
        pre_save.connect(self.set_html, sender=cls)

    def __init__(self, html_field=None, *args, **kwargs):
        self.html_field = html_field
        super().__init__(*args, **kwargs)

MarkdownWorker

Даннй класс отввечает за генерирование html контента из markdown разметки. В данном классе вы можете добавить свои любые дополнительные виды обработки из html, чтобы получить необходимы вам готовый результат. Например, вы можете удалять ссылки или изображения, которые, по вашему мнению, пользователь не может добавлять.

При создании объекта класса MarkdownWorker устанавливается текст для обработки, а далее вызывается метод, который преобразует текст в html.

Также в моём случае используются расширения для:

  • Генерирования таблиц
  • Поддержки кода
  • Добавления переноса
  • Верхнего и нижнего синтаксиса
# -*- coding: utf-8 -*-

import markdown

class MarkdownWorker:
    """
    Markdown converter. It will convert markdown text to html text
    """
    def __init__(self, text):
        self.pre_markdown_text = text
        self.markdown_text = None
        self.make_html_from_markdown()

    def make_html_from_markdown(self):
        if self.pre_markdown_text:
            self.markdown_text = markdown.markdown(
                self.pre_markdown_text,
                extensions=['markdown.extensions.attr_list',
                            'markdown.extensions.tables',
                            'markdown.extensions.fenced_code',
                            'markdown.extensions.nl2br',
                            'superscript',
                            'subscript'],
                output_format='html5'
            )

    def get_text(self):
        return self.markdown_text

Для поддержки всего этого функционала вам необходимо будет установить библиотеки:

  • Markdown
  • MarkdownSubscript
  • MarkdownSuperscript

Возможно ещё beautifulsoup4 , либо это установится в зависимостях, а так честно, не помню.

Применение

А теперь применим данное поле в модели данных.

У нас имеется модель Post, в которой находятся поля:

  • user - внешний ключ на пользователя
  • content - поле, в котором будет содержаться html контент
  • content_markdown - поле, в которое будет сохраняться markdown текст с последующей генерацией html текста. При этом вы видите, что в него передаётся аргумент html_field='content', который говорит, с каким полем предстоит работать MarkdownField.
# -*- coding: utf-8 -*-

from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _

from .fields import MarkdownField


class Post(models.Model):
    user = models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

    content = models.TextField(verbose_name=_('Description'))
    content_markdown = MarkdownField(verbose_name=_('Description - Markdown'), html_field='content')

Заключение

Таким образом вы сможете добавить на свой сайт markdown поля, которые будут автоматически генерировать html контент.
Теперь у вас будет поддержка модного markdown синтаксиса на вашем сайте, который автоматически создаст html контент.

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
B

C++ - Тест 002. Константы

  • Результат:16баллов,
  • Очки рейтинга-10
B

C++ - Тест 001. Первая программа и типы данных

  • Результат:46баллов,
  • Очки рейтинга-6
FL

C++ - Тест 006. Перечисления

  • Результат:80баллов,
  • Очки рейтинга4
Последние комментарии
k
kmssr9 февраля 2024 г. 5:43
Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко5 февраля 2024 г. 12:50
Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 декабря 2023 г. 21:30
Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 декабря 2023 г. 19:38
Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik19 декабря 2023 г. 8:01
Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Сейчас обсуждают на форуме
P
Pisych27 февраля 2023 г. 15:04
Как получить в массив значения из связанной модели? Спасибо, разобрался:))
AC
Alexandru Codreanu19 января 2024 г. 22:57
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…
BlinCT
BlinCT27 декабря 2023 г. 19:57
Растягивать Image на парент по высоте Ну и само собою дял включения scrollbar надо чтобы был Flickable. Так что выходит как то так Flickable{ id: root anchors.fill: parent clip: true property url linkFile p…
Дмитрий
Дмитрий10 января 2024 г. 15:18
Qt Creator загружает всю оперативную память Проблема решена. Удалось разобраться с помощью утилиты strace. Запустил ее: strace ./qtcreator Начал выводиться весь лог работы креатора. В один момент он начал считывать фай…
Evgenii Legotckoi
Evgenii Legotckoi12 декабря 2023 г. 17:48
Побуквенное сравнение двух строк Добрый день. Там случайно не высылается этот сигнал textChanged ещё и при форматировани текста? Если решиать в лоб, то можно просто отключать сигнал/слотовое соединение внутри слота и …

Следите за нами в социальных сетях