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 хостинг.

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

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
d
  • dsfs
  • 26 апреля 2024 г. 14:56

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

  • Результат:80баллов,
  • Очки рейтинга4
d
  • dsfs
  • 26 апреля 2024 г. 14:45

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

  • Результат:50баллов,
  • Очки рейтинга-4
d
  • dsfs
  • 26 апреля 2024 г. 14:35

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

  • Результат:73баллов,
  • Очки рейтинга1
Последние комментарии
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" она решит проблему , лично мне помогло.
Сейчас обсуждают на форуме
IscanderChe
IscanderChe30 апреля 2024 г. 14:22
Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…
G
Gar22 апреля 2024 г. 15:46
Clipboard Как скопировать окно целиком в clipb?
DA
Dr Gangil Academics20 апреля 2024 г. 17:45
Unlock Your Aesthetic Potential: Explore MSC in Facial Aesthetics and Cosmetology in India Embark on a transformative journey with an msc in facial aesthetics and cosmetology in india . Delve into the intricate world of beauty and rejuvenation, guided by expert faculty and …
a
a_vlasov14 апреля 2024 г. 16:41
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел Дорофеев14 апреля 2024 г. 12:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь

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