Jinja render text in HTML preserving line breaks

Question:

I have a simple form like this:

class RecordForm(Form):    
    notes = TextAreaField('Notes')

I record the data in three paragraphs like this:

para1

para2

para3

In the template I would like to see the content of that record in read-only. (Not editable form)

record is this case the model containing the data:

<td>{{ record.notes }}</td>

–>

<td>para1 para2 para3</td>

What can I do to make it to show the multi-lines?

Asked By: Houman

||

Answers:

All whitespace, including newlines, is turned into a single space in HTML.

Your options, from best to worst:

  1. Put white-space: pre-wrap; on the containing element. This tells HTML to show all whitespace exactly as it appears in the source, including newlines. (You could also use a <pre> tag, but that will also disable word-wrapping, which you probably don’t want.)
  2. Treat the plain text as Markdown and throw a Markdown processor at it—one of the things Markdown does is wrap paragraphs in <p>.
  3. In Python-land, do .replace('n', '<br>'). But this leaves you vulnerable to XSS because there might be other HTML-like junk in the string, and fixing that is a bit of a pain.
Answered By: Eevee

As suggested by Martijn Pieters (by linking Flask snippet 28), there is also the possibility to add a custom filter for that. The link is outdated, because Flask Snippets are no longer provided.

So I will provide the snippet from web archive here:

nl2br filter

Posted by Dan Jacob on 2010-06-17 @ 05:03 and filed in Template Tricks

This is a nl2br (newline to <BR>) filter, adapted from the Jinja2 example here:

http://jinja.pocoo.org/2/documentation/api#custom-filters

import re

from jinja2 import evalcontextfilter, Markup, escape

_paragraph_re = re.compile(r'(?:rn|r|n){2,}')

app = Flask(__name__)

@app.template_filter()
@evalcontextfilter
def nl2br(eval_ctx, value):
    result = u'nn'.join(u'<p>%s</p>' % p.replace('n', '<br>n') 
        for p in _paragraph_re.split(escape(value)))
    if eval_ctx.autoescape:
        result = Markup(result)
    return result

The link above about custom-filters seems to be outdated, too. Here is a similar link from the current stable version 1.1: https://flask.palletsprojects.com/en/1.1.x/templating/#registering-filters

I’m not really sure why he uses such a complicated result computation. For me the following code worked and it’s much simpler. Maybe, the variant above is better if you don’t use autoescape (which I do not want to disable)?! Anyway, now both variants are available:

# custom_template_filters.py

from flask import Blueprint
from jinja2 import evalcontextfilter, Markup, escape

blueprint = Blueprint('custom_template_filters', __name__)

@evalcontextfilter
@blueprint.app_template_filter()
def newline_to_br(context, value: str) -> str:
    result = "<br />".join(re.split(r'(?:rn|r|n){2,}', escape(value)))

    if context.autoescape:
        result = Markup(result)

    return result

It is worth mentioning that the code snippet from Dan Jacob uses Python2, and I get the template filters running via Blueprint. For the sake of completeness I also provide the app.py using a factory method:

# app.py

from flask import Flask

def create_app() -> Flask:
    app = Flask(__name__)
    ...
    register_template_filters(flask_app=app)
    return app

def register_template_filters(flask_app: Flask) -> None:
    from . import custom_template_filters
    flask_app.register_blueprint(custom_template_filters.blueprint)
    return None

It is more or less an implementation detail how you will get the context filter working. The main idea is inside the function nlbr or newline_to_br itself. If you get the custom filter working, it is available in all your Jinja templates and you can use it like this:

{{ anystring | newline_to_br }}
Answered By: colidyre

I’ve modified nl2br filter from documentation according to this https://stackoverflow.com/a/60572523/12851576 answer:

import re
from jinja2 import evalcontextfilter
from markupsafe import Markup, escape


@evalcontextfilter
def nl2br(eval_ctx, value):
    """Converts newlines in text to HTML-tags"""
    result = "<br>".join(re.split(r'(?:rn|r|n)', escape(value)))

    if eval_ctx.autoescape:
        result = Markup(result)
    return result

It works for me.
Are there any drawbacks?

Answered By: Vlad
<pre>
{% for line in text.splitlines() %}
{{ line }}
{% endfor %}
</pre>

https://docs.python.org/3/library/stdtypes.html#str.splitlines

Answered By: Damien
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.