How to render content with jinja using flask? [duplicate] - python

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?

All whitespace, including newlines, is turned into a single space in HTML.
Your options, from best to worst:
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.)
Treat the plain text as Markdown and throw a Markdown processor at it—one of the things Markdown does is wrap paragraphs in <p>.
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.

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 }}

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?

Related

flask render_template return ascii code for string

The content of "test.html" is
{{data}}
When I render it using
render_template('test.html',data=u'{"a":12,"b":34}')
the result is
{& #34;a& #34;:12,& #34;b& #34;:34} //I add whitespace between & and # for display
When I render it with
render_template_string('{{data}}',data=u'{"a":12,"b":34}')
the result is
{"a":12,"b":34}
It's difficult to deal with the 1st result string with JSON.parse() in JavaScript. Is that a bug of flask? Or did I deal with it in a wrong way?
When you call render_template, Jinja automatically escapes the string. To stop that, use the safe filter:
{{ data|safe }}
The issue is that when loading from the file the values will be escaped, and this is expected. What I'd recommend, however, is that if you are looking to return JSON do the following instead, as an example:
from flask import jsonify
#app.route('/get_data')
def get_data():
return jsonify({"a":12,"b":34})
http://flask.pocoo.org/docs/0.10/api/#flask.json.jsonify

Flask URL encoding mis [duplicate]

My application frequently takes URL encoded strings as a URL parameter. Often these strings look like paths with a leading slash. IE /file/foo. In flask, I have an endpoint that takes a path parameter that I send a URL encoded path to. So I have something that looks like:
import flask
app = flask.Flask("Hello World")
#app.route("/blah/<path:argument>", methods=["GET"])
def foo(argument):
return "GOT: %s" % argument
if __name__ == "__main__":
app.run(debug=True)
This works great if I visit this URL:
http://localhost:5000/blah/cats%2F
returns:
GOT: cats/
But a leading slash with %2F fails with 404 in the case of GET and 405 in the case of POST. In other words, this 404s:
http://localhost:5000/blah/%2Fcats
In my research on this problem, I was lead to believe here that URL encoding was sufficient to sole the problem. However that doesn't appear to be the case.
This is because of how Werkzeug parses urls. It decodes the encoded slashes before parsing the route, so they still appear as leading slashes. There are bug reports about this:
https://github.com/mitsuhiko/flask/issues/900
https://github.com/mitsuhiko/werkzeug/pull/478
The second link provides a patch to perform this decoding after routing, but it is not merged.
It looks like the best solution at this point is to follow Martijn's answer here.
One way to get around this without defining your own PathConverter is having two route filters:
import flask
app = flask.Flask("Hello World")
#app.route("/blah/<path:argument>", methods=["GET"])
#app.route("/blah//<path:argument>", methods=["GET"])
def foo(argument):
return "GOT: %s" % argument
if __name__ == "__main__":
app.run(debug=True)
Hitting this with:
http://localhost:5000/blah/%2Fcats
Gives me:
GOT: cats
And with:
http://localhost:5000/blah//cats
Gives me:
GOT: cats
But a better (cleaner) solution is probably the one described in this SO answer: Flask route using path with leading slash

Jinja render text in HTML preserving line breaks

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?
All whitespace, including newlines, is turned into a single space in HTML.
Your options, from best to worst:
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.)
Treat the plain text as Markdown and throw a Markdown processor at it—one of the things Markdown does is wrap paragraphs in <p>.
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.
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 }}
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?

Display all jinja object attributes

Is there a way to display the name/content/functions of all attributes of a given object in a jinja template. This would make it easier to debug a template that is not acting as expected.
I am building a website using the hyde framework and this would come in quite handy since I am still learning the intricacies of both jinja and hyde.
Originally, I had thought it would work to use the attr filter, but this seems to require a name value. I would like to to not have to specify the name in order to get all available attributes for the object.
Some google searching showed django syntax looks like the following, but I am not familiar with django so this may only apply to database items. Long story short, I would like a method that works kind of like this for any object named obj
{% for field, value in obj.get_fields %}
{{ field }} : {{ value }} </br>
{% endfor %}
final solution:
#jayven was right, I could create my own jinja2 filter. Unfortunately, using the stable version of hyde (0.8.4), this is not a trivial act of having a filter in the pythonpath and setting a simple yaml value in the site.yaml file (There is a pull-request for that). That being said, I was able to figure it out! So the following is my final solution which ends up being very helpful for debugging any unkown attributes.
It's easy enough to create site-specific hyde extensions just create a local python package with the following directory tree
hyde_ext
__init__.py
custom_filters.py
Now create the extension:
from hyde.plugin import Plugin
from jinja2 import environmentfilter, Environment
debug_attr_fmt = '''name: %s
type: %r
value: %r'''
#environmentfilter
def debug_attr(env, value, verbose=False):
'''
A jinja2 filter that creates a <pre> block
that lists all the attributes of a given object
inlcuding the value of those attributes and type.
This filter takes an optional variable "verbose",
which prints underscore attributes if set to True.
Verbose printing is off by default.
'''
begin = "<pre class='debug'>\n"
end = "\n</pre>"
result = ["{% filter escape %}"]
for attr_name in dir(value):
if not verbose and attr_name[0] == "_":
continue
a = getattr(value, attr_name)
result.append(debug_attr_fmt % (attr_name, type(a), a))
result.append("{% endfilter %} ")
tmpl = Environment().from_string("\n\n".join(result))
return begin + tmpl.render() + end
#return "\n\n".join(result)
# list of custom-filters for jinja2
filters = {
'debug_attr' : debug_attr
}
class CustomFilterPlugin(Plugin):
'''
The curstom-filter plugin allows any
filters added to the "filters" dictionary
to be added to hyde
'''
def __init__(self, site):
super(CustomFilterPlugin, self).__init__(site)
def template_loaded(self,template):
super(CustomFilterPlugin, self).template_loaded(template)
self.template.env.filters.update(filters)
To let hyde know about the extension add hyde_ext.custom_filters.CustomFilterPlugin to the "plugins" list of the site.yaml file.
Lastly, test it out on a file, you can add this to some random page {{resource|debug_attr}} or the following to get even the underscore-attributes {{resource|debug_attr(verbose=True)}}
Of course, I should add, that it seems like this might become much easier in the future whenever hyde 1.0 is released. Especially since there is already a pull request waiting to implement a simpler solution. This was a great way to learn a little more about how to use jinja and hyde though!
I think you can implement a filter yourself, for example:
from jinja2 import *
def show_all_attrs(value):
res = []
for k in dir(value):
res.append('%r %r\n' % (k, getattr(value, k)))
return '\n'.join(res)
env = Environment()
env.filters['show_all_attrs'] = show_all_attrs
# using the filter
tmpl = env.from_string('''{{v|show_all_attrs}}''')
class Myobj(object):
a = 1
b = 2
print tmpl.render(v=Myobj())
Also see the doc for details: http://jinja.pocoo.org/docs/api/#custom-filters

Django: Detect unused templates

Is there a way to detect unused templates in a Django project?
Before Django 1.3, that would have been possible with a simple string-matching function like this one. But since 1.3, there are generic class based views that automatically generate a template_name, if you don't override it (e.g. DetailView).
Also, if you override 3rd party module templates, those templates aren't used anywhere directly in your views.
Maybe it could be done by crawling all URL definitions, loading the corresponding views and getting the template_name from them?
I was curious if you could do this by monkey patching/decorating get_template instead. I think you can, though you have to find all the template loading
functions (I have two in my example below).
I used wrapt when I noticed it went beyond just loader.get_template, but it seems to the trick just fine. Of course, keep this 50000 km away from prod, but...
Now, the thing to follow as well is that I am driving this with unittests and nosetests so, if you have full branch coverage of your template-using Python code, you should be able to get most templates (assuming I didn't miss any get_template-type functions).
in settings.py
This is the "brains" to patch get_template & co.
import wrapt
import django.template.loader
import django.template.engine
def wrapper(wrapped, instance, args, kwargs):
#concatenate the args vector into a string.
# print "\n\n\n\n%s\nI am a wrapper \nusage:%s\n%s\n\n\n\n\n" % ("*"*80, usage, "*"*80)
try:
return wrapped(*args, **kwargs)
finally:
usage = ",".join([unicode(arg) for arg in args if arg])
track_usage(usage)
#you have to wrap whatever is loading templates...
#imported django module + class/method/function path of what needs to be
#wrapped within that module. comment those 2 lines out and you are back to
#normal
wrapt.wrap_function_wrapper(django.template.loader, 'get_template', wrapper)
wrapt.wrap_function_wrapper(django.template.engine, 'Engine.find_template', wrapper)
See safely-applying-monkey-patches-in-python for more details on wrapt. Actually easier to use than to understand the docs, decorators make my brain hurt.
Also, to track which django functions were doing the actual loads I mispelled some template names on purpose in the code and in templates, ran unit tests on it and looked at the stacktraces for missing template exceptions.
This is my rather badly-written function which adds to a set and puts it into
a json output....
def track_usage(usage):
fnp_usage = "./usage.json"
try:
with open(fnp_usage, "r") as fi:
data = fi.read()
#read the set of used templates from the json file
j_data = json.loads(data)
s_used_file = set(j_data.get("li_used"))
except (IOError,),e:
s_used_file = set()
j_data = dict()
s_used_file.add(usage)
#convert the set back to a list for json compatibility
j_data["li_used"] = list(s_used_file)
with open(fnp_usage, "w") as fo:
json.dump(j_data, fo)
and the ouput (with a script to format it):
import sys
import json
fnp_usage = sys.argv[1]
with open(fnp_usage, "r") as fi:
data = fi.read()
#read the set of used templates from the json file
j_data = json.loads(data)
li_used_file = j_data.get("li_used")
li_used_file.sort()
print "\n\nused templates:"
for t in li_used_file:
print(t)
From wrapping the 2 functions above, it seems to have caught extends, %includes and straight get_templates, as well as list-type templates that were being used by class-based views. It even caught my dynamically generated templates which aren't even on the file system but get loaded with a custom loader.
used templates:
bootstrap/display_form.html
bootstrap/errors.html
bootstrap/field.html
bootstrap/layout/baseinput.html
bootstrap/layout/checkboxselectmultiple.html
bootstrap/layout/field_errors.html
bootstrap/layout/field_errors_block.html
bootstrap/layout/help_text.html
bootstrap/layout/help_text_and_errors.html
bootstrap/layout/radioselect.html
bootstrap/whole_uni_form.html
django_tables2/table.html
dynamic_template:db:testdb:name:pssecurity/directive.PrimaryDetails.json
uni_form/layout/div.html
uni_form/layout/fieldset.html
websec/__base.html
websec/__full12.html
websec/__l_right_sidebar.html
websec/bootstrapped_home.html
websec/changedb.html
websec/login.html
websec/requirejs_config.html
websec/topnav.html
websec/user_msg.html
It's not possible to detect unused templates for certain, even in the absence of generic views, because you can always write code like this:
get_template(any_code_you_like()).render(context)
So even prior to Django 1.3 the django-unused-templates application you linked to could only have worked for projects that respected some kind of discipline about the use of templates. (For example, always having a string literal as the template argument to functions like get_template and render_to_response.)
Loading all the views wouldn't be sufficient either: a view may use different templates under different circumstances:
def my_view(request):
if request.user.is_authenticated():
return render(request, 'template1.html')
else:
return render(request, 'template2.html')
And of course templates may not be used by views at all, but by other parts of the system (for example, e-mail messages).

Categories