Как предотвратить автоматическое экранирование базовых строк Django

Приложение Django Basic Inlines отображает предварительно определенный шаблон из синтаксиса псевдо-HTML на основе комбинации приложение/модель/идентификатор. Например, если вы пишете сообщение в блоге, вы можете вставить изображение, которое было сохранено в вашей модели изображения:

# In the admin
This is the body of my post.

<inline type="media.image" id="1" class="full">

Затем шаблон принимает фильтр render_inlines, который необходимо пометить safe, чтобы правильно отображать HTML:

# Template
{{ post.body|render_inlines|safe }}

Но даже с safe фильтр по-прежнему избегает HTML, создавая &lt;p&gt;&lt;img src="..."&gt;&lt;p&gt; в исходном коде.

Согласно документам, фильтр должен использовать mark_safe, чтобы предотвратить автоматическое экранирование на уровне фильтра, но функция inlines в parser.py уже использует mark_safe.

Есть ли что-то, что еще необходимо в Django 1.4, чтобы остановить автоматическое экранирование на пользовательском слое фильтра? Я не могу избавиться от этого автоматического экранирования ни в

Пробовал использовать autoescape=None, тоже не помогло.


person richardcornish    schedule 30.03.2012    source источник
comment
Каков результат удаления сейфа и {% autoescape off %}?   -  person okm    schedule 30.03.2012
comment
Ничего, тот же результат, поэтому я считаю, что экранирование происходит на уровне фильтра, а не на уровне шаблона. Если что-то уже экранировано, добавление safe или отключение autoescape в шаблоне ничего не делает. Это уже безопасно.   -  person richardcornish    schedule 31.03.2012


Ответы (3)


Я поддерживаю форк встроенного приложения. Ричард связался со мной по поводу этой проблемы, и я смог отследить ее до BeautifulSoup, а не от Django.

Проблема заключалась в том, что метод BeautifulSoup replaceWith() использовался для замены встроенной разметки отображаемым шаблоном. Результатом render_to_string() является, конечно же, строка. Когда replaceWith() получает строку, она преобразуется в NavigableString. Поскольку BeautifulSoup ожидает, что NavigbleString будут строками, он предполагает, что они небезопасны, и экранирует любые символы HTML. В результате значение, возвращаемое функцией Inline inlines(), содержало группу &gt; и &lt;, а не < и >.

Я не заметил этой проблемы в Django 1.3. Когда я посмотрел, BeautifulSoup действительно возвращал экранированный HTML. Фильтр шаблонов Django |safe, должно быть, не экранировал ранее экранированный HTML. В Django 1.4 этого больше не происходит. (А так не должно быть!)

Мое решение для этого состоит в том, чтобы проанализировать входящее значение с помощью BeautifulSoup и использовать BeautifulSoup для поиска всей встроенной разметки, как и раньше. Вместо того, чтобы использовать метод BeautifulSoup replaceWith() для замены встроенной разметки отображаемым встроенным шаблоном, я теперь просто использую старый добрый str.replace() Python. Мне кажется немного хромым преобразовывать проанализированный суп обратно в строку, а затем выполнять замену строки. Но это работает. У меня есть соблазн вообще отказаться от BeautifulSoup и найти встроенную разметку с регулярными выражениями, но мы все знаем, чем это заканчивается. Если у кого-то есть идея получше, я весь внимание!

Первоначально исправление было реализовано в этой фиксации. Я улучшил его в следующем коммите, но, видимо, StackOverflow позволяет мне опубликовать максимум две ссылки, поэтому вам придется найти эту ссылку самостоятельно!

person Pig Monkey    schedule 03.04.2012
comment
Django |safe никогда не выполнял "неэкранирование", он просто помечает текст как не нуждающийся в дальнейшем экранировании. - person spookylukey; 23.06.2012

Другое решение этой проблемы — превратить новый код в объект BeautifulSoup и заменить его указанным объектом. Таким образом, красивый суп, кажется, ведет себя правильно.

Это дает вам экранированный html:

soup = BeautifulSoup(html_doc)
body = soup.body
new_html = """<p> this is some deap code</p><a href="#">Pointless even</a>"""
body.replaceWith(new_html)

Это дает вам ваш html без экранирования:

soup = BeautifulSoup(html_doc)
body = soup.body
new_html = """<p> this is some deap code</p><a href="#">Pointless even</a>"""
body.replaceWith(BeautifulSoup(new_html))
person yarbelk    schedule 13.04.2012

Это из-за render_to_string здесь. Перейдите к inlines/app_model.html и inlines/default.html и добавьте |safe после переменных содержимого.

person ilvar    schedule 31.03.2012
comment
Вы имеете в виду app/templates/app/model_detail.html и app/templates/inlines/default.html, верно? Отметить эти safe? Это то, что я уже делал. Кто-то помогал мне, и это могла быть проблема с BeautifulSoup 3.2.0 по сравнению с BeautifulSoup 3.2.1, и как replaceWith был изменен в указанной вами строке. - person richardcornish; 31.03.2012
comment
Не model_detail.html, а шаблон, который используется в этой строке. Вы можете использовать print или pdb, чтобы проверить это. В этой строке ваши данные отображаются в шаблоне, а после этого результат возвращается в то место, где вы вызываете встроенные строки. Вы должны отключить автоматическое экранирование как во внешних, так и во внутренних шаблонах. - person ilvar; 01.04.2012