Evgenii Legotckoi
Evgenii Legotckoi08 лютого 2022 р. 06: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

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

  • Уцінка
  • MarkdownSubscript
  • Позначка Надіндекс

Можливо ще 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 хостинг.

Вам це подобається? Поділіться в соціальних мережах!

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
Дмитрий

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

  • Результат:60бали,
  • Рейтинг балів-1
Дмитрий

C++ - Тест 003. Условия и циклы

  • Результат:92бали,
  • Рейтинг балів8
d
  • dsfs
  • 26 квітня 2024 р. 14:56

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

  • Результат:80бали,
  • Рейтинг балів4
Останні коментарі
k
kmssr09 лютого 2024 р. 05:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 12:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 21:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 19:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik19 грудня 2023 р. 08:01
Qt/C++ - Урок 056. Підключення бібліотеки Boost в Qt для компіляторів MinGW і MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
G
George1307 травня 2024 р. 10:27
добавить qlineseries в функции в функции: "GPlotter::addSeries(QString title, QVector &arr)" я вызываю метод setChart(...), я в конструктор передал адрес на QChartView элемент
BlinCT
BlinCT05 травня 2024 р. 15:46
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
PS
Peter Son04 травня 2024 р. 03:57
Best Indian Food Restaurant In Cincinnati OH Ready to embark on a gastronomic journey like no other? Join us at App india restaurant and discover why we're renowned as the Best Indian Food Restaurant In Cincinnati OH . Whether y…
Evgenii Legotckoi
Evgenii Legotckoi03 травня 2024 р. 00:07
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.
IscanderChe
IscanderChe30 квітня 2024 р. 14:22
Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…

Слідкуйте за нами в соціальних мережах