3

The Django Basic Inlines app renders a pre-determined template from a pseudo-HTML syntax, based on an app/model/id combination. For example, if you're writing a blog post, you can insert an image that was saved in your image model:

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

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

The template then takes a render_inlines filter, which requires to be marked safe so as to render the HTML properly:

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

But even with safe, the filter still escapes the HTML, creating &lt;p&gt;&lt;img src="..."&gt;&lt;p&gt; in the source.

According to the docs, the filter should use mark_safe to prevent autoescaping at the filter level, but the inlines function in parser.py already uses mark_safe.

Is there something that is further needed in Django 1.4 to stop autoescaping at the custom filter layer? I can't seem to get rid of this autoescaping, either at the

I tried using autoescape=None, which didn't seem to help either.

richardcornish
  • 164
  • 1
  • 14
  • What's the result by removing safe and {% autoescape off %}? – okm Mar 30 '12 at 12:53
  • Nothing, the same result, which is why I believe the escaping is happening at the filter level and not at the template. If something is already escaped, putting `safe` or turning off `autoescape` in the template doesn't do anything. It's already safe. – richardcornish Mar 30 '12 at 22:54

3 Answers3

2

Another solution to this is to turn the new code into a BeautifulSoup object, and replaceWith said object. This way beautiful soup seems to behave correctly.

This gives you escaped 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)

This gives you your html unescapped:

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))
yarbelk
  • 7,215
  • 6
  • 29
  • 37
1

I maintain a fork of the Inline app. Richard contacted me about this problem and I was able to trace it back to BeautifulSoup, not Django.

The problem was that BeautifulSoup's replaceWith() method was being used to replace the inline markup with the rendered template. The result of render_to_string() is, of course, a string. When replaceWith() receives a string, it turns it into a NavigableString. Since BeautifulSoup expects NavigbleStrings to be strings, it assumes they are unsafe and escapes any HTML characters. The result is that the value being returned by Inline's inlines() function had a bunch of &gt; and &lt; in it rather than < and >.

I didn't notice this problem in Django 1.3,. When I looked, BeautifulSoup was indeed returning escaped HTML. Django's |safe template filter must have been unescaping the previously escaped HTML. In Django 1.4, it no longer does that. (And it shouldn't do that!)

My fix for this is to parse the incoming value with BeautifulSoup and use BeautifulSoup to find all the inline markup, just like before. Rather than using BeautifulSoup's replaceWith() method to replace the inline markup with the rendered inline template, I'm now just using Python's plain old str.replace(). It feels a bit lame to me, converting the parsed soup back to a string and then doing the string replacement. But it works. I'm partly tempted to just do away with BeautifulSoup altogether and find the inline markup with regular expressions but we all know how that ends. If anybody has a better idea, I'm all ears!

The fix was initially implented in this commit. I improved it in the following commit, but apparently StackOverflow is only allowing me to post a maximum of two links, so you'll have to find that one yourself!

Community
  • 1
  • 1
Pig Monkey
  • 26
  • 2
0

It's because of the render_to_string here. Go to inlines/app_model.html and inlines/default.html and add |safe after content variables there.

ilvar
  • 5,718
  • 1
  • 20
  • 17
  • You mean app/templates/app/model_detail.html and app/templates/inlines/default.html, right? Mark those `safe`? That's what I was already doing. Someone was helping me and it could be an issue with BeautifulSoup 3.2.0 versus BeautifulSoup 3.2.1, and how [`replaceWith`](http://www.crummy.com/software/BeautifulSoup/bs3/documentation.html#Replacing%20one%20Element%20with%20Another) was changed in the line you cited. – richardcornish Mar 31 '12 at 07:21
  • Not `model_detail.html` but the template which is used on that line. You can use `print` or pdb to check it out. On that line your data is being rendered into the template and after that result is being returned in the place where you are calling inlines. You shoud have autoescaping disabled in both external and internal templates. – ilvar Apr 01 '12 at 02:21