How to use a Jinja2 extension direct from a Flask render_template - python

I'm working on a Flask app that is working just fine, until I try to add the following line to a template to be rendered:
{% do totals.update({tier: 0}) %}
The current code for rendering the template uses Flask's render_template():
from flask import Flask, Response, request, session
from flask import render_template
app = Flask(__name__)
..
return render_template(<template.htlm>,...)
This fails with with following error:
TemplateSyntaxError: Encountered unknown tag 'do'. Jinja was looking for the following tags: 'endfor' or 'else'. The innermost block that needs to be closed is 'for'.
The obvious fix is to add the jinja2.ext.do extension to jinja. I have been able to do so successfully using Jinja2 directly, as per:
from jinja2 import Environment, PackageLoader
ENV = Environment(loader=PackageLoader('ticket_app', 'templates'), extensions=['jinja2.ext.do'])
...
TEMP = ENV.get_template('div_' + div_id + '.html')
return TEMP.render(sales_new=sales_new, event_config=event_config)
However, I would prefer to not use Jinja2 directly... The app was only using Flask and render_template() before, and as render_template() uses Jinja2 under the hood (as far as I understand) it seems that it should be possible to make render_template() understand the jinja2.ext.do extension (or any other extension for that matter).
So far, I've tried the following:
app = Flask(__name__)
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.config['EXPLAIN_TEMPLATE_LOADING'] = True
env = app.jinja_env
env.add_extension('jinja2.ext.do')
While the above does not throw an error, it is also not causing it to make render_template() understand the jinja2.ext.do extension.
Any suggestions? Should this be possible? If so, how?

Update for Flask v.2.0 :
Since v.2.0, the below solution raise a KeyError: 'extensions' exception. Try this instead.
For Flask v.1.1, you can directly access to the Jinja extensions loaded by Flask with the Flask.jinja_options dictionary. In your case, adding just this line should do the trick:
app = Flask(__name__)
app.jinja_options['extensions'].append('jinja2.ext.do')
Make sure you update your Flask (using pip: pip install -U Flask).

I actually found that the option that Zatiranyk outlined broke sometimes (specifically when I tried to use Flask-SocketIO.
app.jinja_options['extensions'].append('jinja2.ext.do')
The solution that I found was to use the below option instead:
app.jinja_env.add_extension('jinja2.ext.do')

Related

How would I serve this html file with Flask (nota a template)

I'm using apidoc to generate a documentation website. On running, it creates a folder called doc, which contains an index.html file and accompanying css and js.
I'd like to be able to serve this folder with a Flask server, but can't work out how to do it.
My folder structure looks like this
-root
--- doc/ #contains all of the static stuff
--- server.py
I've tried this, but can't get it to work:
app = Flask(__name__, static_url_path="/doc")
#app.route('/')
def root():
return app.send_from_directory('index.html')
One of the problems is that all of the static files referenced in the index.html generated by apidoc are relative to that page, so /js/etc. doesn't work, since it's actually /doc/js...
It would be great if someone could help me with the syntax here. Thanks.
I spot three problems in code.
a) you do not need to use static_url_path, as send_from_directory is independent of it
b) when I try to run above code, and go to /, I get a AttributeError: 'Flask' object has no attribute 'send_from_directory' - this means translates to app.send_from_directory is wrong - you need to import this function from flask, ie from flask import send_from_directory
c) when I then try to run your code, I get a TypeError: send_from_directory() missing 1 required positional argument: 'filename', which means send_from_directory needs another argument; it needs both a directory and a file
Putting this all together you get something like this:
from flask import Flask
from flask import send_from_directory
app = Flask(__name__)
#app.route("/")
def index():
return send_from_directory("doc", "index.html")
As a takeway (for myself):
reading the documentation helps a lot ( https://flask.palletsprojects.com/en/1.1.x/api/ )
having a close look at the - at first scary - error messages gives really good hints on what to do

Unable to serve static file from flask server

I have a index.html file, which has the absolute path 'c:\project\web\frontend\index.html'
I am trying to return it using the following function
#webserver.route('/')
def home()
return webserver.send_static_file(path)
I have verified that the path is correct by accessing it directly in the browser.
I have tried to replace '\' with '/' without any luck.
It is running on a windows machine.
If you look at flask's documentation for send_static_file. You'll see that it says that it's used internally by flask framework to send a file to the browser. If you want to render an html, it's common to use render_template. You need to make sure that your index.html is in a folder called templates first.
So I would do the following:
#webserver.route('/')
def home()
return flask.render_template('index.html')
I had to define the path to be the static_folder, when creating the flask object. Once I defined the folder to be static, the html page was served.

Python script works as pure python but not with Flask

I'm working on some code that pulls course info from Canvas. As pure python, it works fine. If I try to incorporate it with Flask, I get the following error
requests.exceptions.MissingSchema: Invalid URL 'run/api/v1/courses/1234567': No schema supplied. Perhaps you meant http://run/api/v1/courses/1234567?
This is the code in question:
Canvas file
import sys
from canvasapi import Canvas
def getinfo():
canvasurl = "https://canvas.instructure.com/";
canvastoken = #Redacted for this example
try:
canvastoken = sys.argv[1];
canvasurl = sys.argv[2];
except:
print()
#Create a new canvas object passing in the newly aquired url and token
canvas = Canvas(canvasurl, canvastoken);
#print(canv)
# Create a new course oject -- passing in course number as a parameter
# Course number is currently hard coded
print(canvas.get_course(1234567))
Flask file code (the file that I'm trying to run):
from flask import Flask
import canvas
canvas.getinfo()
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
No schema provided usually means you haven't specified the http:// or https:// in the URL.
In the code you provided, I don't see any reference to a run/api/v1/courses/1234567. One possibility is if you are using the url_for method from requests anywhere in your code, try setting _external=True:
url = url_for('relativeURL', _external=True)
This allows Flask to construct an absolute URL (i.e., a URL with domain included).
If you aren't using url_for, check other places in your code where you might be omitting the http or https from the URL.
If you update your question to include the part that refers to the offending URL, we might be able to provide more specific help.

Unable to render in flask interactive html outputs produced by R plotly package

So I am using python flask to develop a web application that asks the user for data, analyse the data in the backend using BLAST and R, and outputs interactive HTML plots that are stored in a local location (Templates) and to be displayed to the user.
Everything up to R outputs runs smoothly. I have opened the HTML files and confirmed the R codes ran as expected and the produced graphs are interactive. However when I render these HTMLs through flask the browser returns a black page. Using send_file was also fruitless to display the plots. I have confirmed that the path to the Java scripts for the interactive plot are in the same folder.
Going through the developers tool console reads the following error:
htmlwidgets.js:1 Uncaught SyntaxError: Unexpected token <
jquery.min.js:1 Uncaught SyntaxError: Unexpected token <
datatables.js:1 Uncaught SyntaxError: Unexpected token <
jquery.dataTables.min.js:1 Uncaught SyntaxError: Unexpected token <
Can someone please advise on how to successfully get the interactive R HTML outputs to show in flask?
When you use send_file or send_from_directory you would also need to serve the JavaScript libraries needed for the plot, this might be the reason for the blank page.
The same is true for render_template but according to the error messages it seems more like that your JS libraries were also rendered as templates and some < or > character got added or removed.
Try the following snippet which works for me and perhaps gives you a good start.
A very simple R script to create a standalone HTML file with a Plotly plot. A file called test.html is created in the /tmp directory. All JavaScript libraries and CSS files are in /tmp/test_files.
library(plotly)
library(htmlwidgets)
myData <- data.frame(x=c(1,2,3), y=c(3,2,1))
local_path <- '/tmp/'
ply <- plot_ly(myData, x = ~x, y = ~y, type='scatter', mode='markers+line')
saveWidget(widget=ply, file=paste(local_path, "test.html", sep=""), selfcontained = FALSE)
A minimal Flask server snippet to serve the plot and the JavaScript libraries is below. Starting flask and going in the browsesr to http://localhost:5000/ would show the plot and provide all need libraries.
from flask import Flask
from flask import send_from_directory
app = Flask(__name__)
#app.route('/')
def plotly():
return send_from_directory('/tmp/', 'test.html')
#app.route('/test_files/<path:path>')
def send_js(path):
return send_from_directory('/tmp/test_files', path)

Using sass with Flask and jinja2

I would like to include a sass compiler in my Flask application. Is there a generally accepted way of doing this?
Flask-Assets extension (which uses webassets library) can be used for that. Here's how to configure it to use pyScss compiler (implemented in Python) for SCSS:
from flask import Flask, render_template
from flask.ext.assets import Environment, Bundle
app = Flask(__name__)
assets = Environment(app)
assets.url = app.static_url_path
scss = Bundle('foo.scss', 'bar.scss', filters='pyscss', output='all.css')
assets.register('scss_all', scss)
And in the template include this:
{% assets "scss_all" %}
<link rel=stylesheet type=text/css href="{{ ASSET_URL }}">
{% endassets %}
SCSS files will be compiled in debug mode as well.
pyScss only supports SCSS syntax, but there are other filters (sass, scss and compass) which use the original Ruby implementation.
Some things have changed since the question was answered in 2013.
You can't have scss installed at the same time as pyscss and expect the pyscss filter to work like in the accepted answer.
scss = Bundle('foo.scss', 'bar.scss', filters='pyscss', output='all.css')
I was getting an error that ended in:
File "/home/sri/crap/example/flask/lib/python2.7/site-packages/webassets/filter/pyscss.py", line 110, in setup
scss.config.STATIC_ROOT = self.static_root or self.ctx.directory
You have to remove scss (i.e. pip uninstall scss) and be sure that pyscss is installed (i.e. pip install pyscss).
Also note that you will have to set some environment variables in order to get pyscss to work as well:
app = Flask(__name__)
assets = Environment(app)
assets.url = app.static_url_path
scss = Bundle('index.scss', filters='pyscss', output='all.css')
assets.config['SECRET_KEY'] = 'secret!'
assets.config['PYSCSS_LOAD_PATHS'] = assets.load_path
assets.config['PYSCSS_STATIC_URL'] = assets.url
assets.config['PYSCSS_STATIC_ROOT'] = assets.directory
assets.config['PYSCSS_ASSETS_URL'] = assets.url
assets.config['PYSCSS_ASSETS_ROOT'] = assets.directory
assets.register('scss_all', scss)
see the documentation on the pyscss filter for more info: http://webassets.readthedocs.io/en/latest/builtin_filters.html#pyscss
I hope that this save someone else a lot of time because I wasted a whole day on it.
I think the most pythonic approach is this one line solution using libsass. After you import sass simply use the compile method with the dirname keyword argument, like this:
sass.compile(dirname=('path/to/sass', 'path/to/css'))
You also have the option to set the output style, for example:
sass.compile(dirname=('path/to/sass', 'path/to/css'), output_style='compressed')
If you want to watch a file or directory for automatic compilation on every edit use boussole.
Currently, exist a better approach for this issue, the extion Flask-Scss.
You just have to install it: pip install Flask-Scss
And instanciate a Scss object after configuring the application (probably in your manage.py file):
from flask import Flask
from flask.ext.scss import Scss
app = Flask(__name__)
Scss(app)
By default, the extension will look for your .scss files in {app.root_dir}/assets/scss or {app.root_dir}/assets and will put the generate .css files in {default_static_dir}/css or {default_static_dir}.
Libsass is a good solution for this.
# Your __init__.py file
from flask import Flask
from sassutils.wsgi import SassMiddleware
app = Flask(__name__)
app.wsgi_app = SassMiddleware(app.wsgi_app, {
'myapp': ('static/sass', 'static/css', '/static/css')
})
# Your HTML (Jinja) file
<link href="{{ url_for('static', filename='css/style.scss.css') }}"
rel="stylesheet" type="text/css">
More info:
https://sass.github.io/libsass-python/index.html

Categories

Resources