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
AD

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

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

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

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

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

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 22:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi01 листопада 2024 р. 00:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 18:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 17:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 21:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi25 червня 2024 р. 01:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 17:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 13:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 19:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

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