Тег шаблона Django для отображения содержимого дословно, а также в виде рендеринга
Я пытаюсь сделать шаблонный тег, который будет показывать участок кода дословно, а также рендерить его, это поможет мне продемонстрировать некоторый код для моего собственного инструментария.
Допустим, у меня есть следующее в шаблоне:
{% example %}import * as fancy from "{% static 'jslib/fancy.min.js' %}";{% endexample %}
Я хочу вывести его в виде (я добавлю несколько div'ов и стилизацию, но это упрощение проблемы):
import * as fancy from "{% static 'jslib/fancy.min.js' %}";
import * as fancy from "/static/jslib/fancy.min.js";
Я посмотрел на тег django {% verbatum %}
и попытался скопировать логику:
# django.template.defaulttags.py
@register.tag
def verbatim(parser, token):
nodelist = parser.parse(('endverbatim',))
parser.delete_first_token()
return VerbatimNode(nodelist.render(Context()))
nodelist.render(Context())
выводит текстовые узлы для тега django verbatim, но если я скопирую код, мой пример тега выведет скажем StaticNodes
и другие типы узлов.
Причина в том, что я думаю, что парсер django имеет некоторые специальные проверки, чтобы увидеть, является ли это дословным узлом и обрабатывает его по-разному, как показано в коде ниже:
# django.template.base.py -> def create_token
if self.verbatim:
# Then a verbatim block is being processed.
if content != self.verbatim:
return Token(TokenType.TEXT, token_string, position, lineno)
# Otherwise, the current verbatim block is ending.
self.verbatim = False
elif content[:9] in ('verbatim', 'verbatim '):
# Then a verbatim block is starting.
self.verbatim = 'end%s' % content
return Token(TokenType.BLOCK, content, position, lineno)
Это означает, что я проверил узлы списка узлов на предмет наличия у них какого-то метода "получить исходную строку", но ничего не нашел.
Как сделать тег, чтобы получить отрендеренный и неотрендеренный текст внутри пары тегов?
Самое неэлегантное решение, забейте на него. Просто загрузите файл и используйте regex для удаления части
import re
from django import template
register = template.Library()
@register.tag
def story(parser, token):
"""Usage: {% story verbatim_1 %} <div>{{ foo }}</div> {% endstory %}
@verbatim_id: str
Attribute value that starts with the text 'verbatim', following it
should be a unique id, e.g. `verbatim_1`, `verbatim_2` etc.
This unique verbatim id is used to open the file and perform a regex
match to find the code and read it verbatim.
"""
bits = token.contents.split()
verbatim_id = next(filter(lambda x: 'verbatim' in x, bits), None)
nodelist = parser.parse(('endstory',))
parser.delete_first_token()
return StoryNode(nodelist,verbatim_id)
class StoryNode(template.Node):
def __init__(self, nodelist, dark, verbatim_id):
self.nodelist = nodelist
self.verbatim_id = verbatim_id
def render(self, context):
example = self.nodelist.render(context)
if self.verbatim_id:
file = self.origin.name
with open(file, 'r') as f:
content = f.read()
pattern = r'{%\s*stZory[^%]*' + self.verbatim_id + r'[^%]*%}(.+?){% endstory %}'
match = re.search(pattern, content, flags=re.M | re.DOTALL)
if not match:
raise ValueError(
f"ERROR: Could not find verbatim_id: '{self.verbatim_id}', line: {self.token.lineno}, file: {self.origin.name}"
)
inner_html = match.group(1).strip()
code = inner_html
else:
code = example
print('The verbatim code is', code)
print('The rendered example is', example)
return mark_safe('')