我希望为我的独立应用程序生成由Django模板系统预处理的人类可读的HTML和CSS代码(正确缩进)。
我已经从django.template.base模块中找到的NodeList类修改了render方法。我的代码似乎工作正常,但我使用猴子修补来替换旧的渲染方法。
在这种情况下,是否有更优雅的方式不使用猴子修补?或者猴子修补可能是最好的方式吗?
我的代码如下所示:
'''
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
答案 0 :(得分:3)
修改模板层可以,但不是最佳的,因为它只是处理节点的呈现方式,而不是整个文档。我建议你的项目使用writing custom middleware来打印html和css页面的渲染响应。
您的中间件需要实现process_template_response
,该SimpleTemplateResponse
应该用于查看和更新is_rendered
对象:
.html
属性以查看是否已呈现回复.css
属性template_name
,content_type
)
mimetype
属性(Django 1.5)或{{1}} 我认为中间件是一个更优雅的解决方案,因为这最终不会对您的文件进行词法更改。它完全与确定模板内容的逻辑(它没有业务存在的地方)分开。最后,你希望你的所有html和css看起来都很棒,那么为什么要首先将它与你的模板联系起来呢?
答案 1 :(得分:1)
您可以使用类继承来创建不同的NodeList
,但可能需要在另一端进行一些修补。您的解决方案看似简单明了。
class MyNodeList(NodeList):
def render(self, context):
# call super if you require so
# your reindent functionality