Evgenii Legotckoi
Evgenii Legotckoi8 февраля 2022 г. 6: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 хостинг.

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

Комментарии

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

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:50баллов,
  • Очки рейтинга-4
m
  • molni99
  • 26 октября 2024 г. 7:37

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80баллов,
  • Очки рейтинга4
m
  • molni99
  • 26 октября 2024 г. 7:29

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:20баллов,
  • Очки рейтинга-10
Последние комментарии
ИМ
Игорь Максимов22 ноября 2024 г. 17:51
Django - Урок 017. Кастомизированная страница авторизации на Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 октября 2024 г. 19:37
Django - Урок 064. Как написать расширение для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 октября 2024 г. 14:19
Читалка fb3-файлов на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов5 октября 2024 г. 13:51
Django - Урок 064. Как написать расширение для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55 июля 2024 г. 17:02
QML - Урок 016. База данных SQLite и работа с ней в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Сейчас обсуждают на форуме
m
moogo22 ноября 2024 г. 13:17
Mosquito Spray System Effective Mosquito Systems for Backyard | Eco-Friendly Misting Control Device & Repellent Spray - Moogo ; Upgrade your backyard with our mosquito-repellent device! Our misters conce…
Evgenii Legotckoi
Evgenii Legotckoi24 июня 2024 г. 21:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 ноября 2024 г. 12:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject4 июня 2022 г. 9:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…

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