Правильный отступ в шаблонах Django (без мартышек-патчей)?

Я хочу сгенерировать читаемый человеком код HTML и CSS (с соответствующим отступом), предварительно обработанный системой шаблонов Django для моего отдельного приложения.

Я изменил метод рендеринга из класса NodeList, найденного в модуле django.template.base. Кажется, мой код работает правильно, но я использую monkey-patching для замены старого метода рендеринга.

Есть ли более элегантный способ, который не использует патч обезьян в этом случае? Или, может быть, мартышка - это лучший способ?

Мой код выглядит так:

'''
This module monkey-patches Django template system to preserve
indentation when rendering templates.
'''

import re

from django.utils.encoding import force_text
from django.utils.safestring import mark_safe
from django.template.loader import render_to_string
from django.template import Node, NodeList, TextNode
from django.template.loader_tags import (BlockNode, ConstantIncludeNode,
                                         IncludeNode)


NEWLINES = re.compile(r'(\r\n|\r|\n)')
INDENT = re.compile(r'(?:\r\n|\r|\n)([\ \t]+)')


def get_indent(text, i=0):
    '''
    Depending on value of `i`, returns first or last indent
    (or any other if `i` is something other than 0 or -1)
    found in `text`. Indent is any sequence of tabs or spaces
    preceded by a newline.
    '''
    try:
        return INDENT.findall(text)[i]
    except IndexError:
        pass


def reindent(self, context):
    bits = ''
    for node in self:
        if isinstance(node, Node):
            bit = self.render_node(node, context)
        else:
            bit = node
        text = force_text(bit)

        # Remove one indentation level
        if isinstance(node, BlockNode):
            if INDENT.match(text):
                indent = get_indent(text)
                text = re.sub(r'(\r\n|\r|\n)' + indent, r'\1', text)

        # Add one indentation level
        if isinstance(node, (BlockNode, ConstantIncludeNode, IncludeNode)):
            text = text.strip()
            if '\r' in text or '\n' in text:
                indent = get_indent(bits, -1)
                if indent:
                    text = NEWLINES.sub(r'\1' + indent, text)

        bits += text

    return mark_safe(bits)


# Monkey-patching Django class
NodeList.render = reindent

2 ответа

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

Ваше промежуточное ПО нужно будет реализовать process_template_response который должен использоваться для просмотра и обновления SimpleTemplateResponse объект:

  • Проверить is_rendered атрибут, чтобы увидеть, если ответ был представлен
  • Проверьте тип документа одним из следующих способов:
    • Ищете нужный тип файла (.html, .css) в конце template_name атрибут
    • Глядя на content_type атрибут (Django 1.5) или, возможно, mimetype для более старых установок
  • Переформатируйте и обновите отрендеренный документ, чтобы он выглядел великолепно ( Beautiful Soup отлично подходит для HTML, но вам нужно будет выбрать нужный вам симпатичный принтер или свернуть свой собственный).

Я думаю, что Middleware - гораздо более элегантное решение, потому что в конечном итоге оно не вносит лексических изменений в ваши файлы. Он полностью отделен от логики, которая определяет содержание вашего шаблона (где он не имеет бизнеса). Наконец, вы хотите, чтобы ВСЕ ваши html и css выглядели великолепно, так зачем сначала связывать это с вашими шаблонами?

Вы можете использовать наследование классов для создания другого NodeList но это вероятно потребует некоторого исправления на другом конце. Ваше решение кажется простым и понятным.

class MyNodeList(NodeList):
    def render(self, context):
        # call super if you require so
        # your reindent functionality
Другие вопросы по тегам