Jinja render text in HTML preserving line breaks

后端 未结 3 1503
旧时难觅i
旧时难觅i 2020-11-30 12:15

I have a simple form like this:

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

I record the data in three paragraphs like

相关标签:
3条回答
  • 2020-11-30 13:03

    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.
    0 讨论(0)
  • 2020-11-30 13:03

    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'(?:\r\n|\r|\n){2,}')
    
    app = Flask(__name__)
    
    @app.template_filter()
    @evalcontextfilter
    def nl2br(eval_ctx, value):
        result = u'\n\n'.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'(?:\r\n|\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 }}
    
    0 讨论(0)
  • 2020-11-30 13:07

    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'(?:\r\n|\r|\n)', escape(value)))
    
        if eval_ctx.autoescape:
            result = Markup(result)
        return result
    

    It works for me. Are there any drawbacks?

    0 讨论(0)
提交回复
热议问题