jinja2 link to static files - python

I am trying to understand how to create a link to static files in jinja2.
Everything I look up relates to Flask whereas I am using just webapp2 at this stage.
My main.py file looks as follows:
import os
import urllib
from google.appengine.api import users
from google.appengine.ext import ndb
import jinja2
import webapp2
JINJA_ENVIRONMENT = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
extensions=['jinja2.ext.autoescape'],
autoescape=True)
class MainPage(webapp2.RequestHandler):
def get(self):
template = JINJA_ENVIRONMENT.get_template('/templates/base.html')
self.response.out.write(template.render())
class ConsultsPage(webapp2.RequestHandler):
def get(self):
template = JINJA_ENVIRONMENT.get_template('/templates/consults.html')
self.response.out.write(template.render())
class CreateConsultPage(webapp2.RequestHandler):
def get(self):
template = JINJA_ENVIRONMENT.get_template('/templates/create-consult.html')
self.response.out.write(template.render())
app = webapp2.WSGIApplication([
('/', MainPage),
('/consults', ConsultsPage),
('/schedule/create-consult', CreateConsultPage)
], debug=True)
My base.html template contains the links to the static files in "/css", "/js" "/images" etc.
When I look at the localhost:8080/ and localhost:8080/consults all the static files are working. Page looks fine.
However the next level in the structure localhost:8080/consults/create-consult is not linking to static files.
When I view source I see that the css link has rendered as localhost:8080/consults/css/style.css , when the actual location is localhost:8080/css/style.css.
I understand I may need to make all links dynamic via some environment variable called uri_for, but I can't find the correct way to implement this.
I tried to replace my css link with
href="{{ uri_for('static', filename='css/screen.css') }}"
I was told by App Engine uri_for not set.
Basically would like to know the correct process for setting uri_for and then how to incorporate it in the paths for my links to static files.
Any help appreciated.

uri_for() is a Flask-specific function; it matches the name static to a route, which in turn then can be used to generate a path (like /static/css/screen.css if the static route is configured to handle /static/<path:filename> urls).
You just need to hardcode the path as /css/screen.css, no need for functions.
Note the leading /; that makes it an absolute path, relative to your current host. For a page at http://localhost:8080/foo/bar, such a path is then prefixed with http://localhost:8080 to form http://localhost:8080/css/screen.css. When you deploy to the app engine, the hostname will be different.
You could store a prefix URL or path in a global, so you can easily swap out the path for a CDN later:
JINJA_ENVIRONMENT.globals['STATIC_PREFIX'] = '/'
and use that in your templates:
<style src="{{ STATIC_PREFIX }}css/screen.css"></style>
You can now alter all such URLs in one place, by setting the STATIC_PREFIX to a different value, including http://somecdn.cdnprovider.tld/prefix/.

Related

Flask: serve assets without leading slash using url_for

I'm working on a Flask app that uses url_for to specify the route to some static assets (js, css, etc). Here's an example from one of the templates:
<script src='{{ url_for('static', filename='js/search.js') }}'></script>
When this gets rendered into html, the path looks like this:
<script src='/static/js/search.js'></script>
Is it possible to modify this behavior such that the leading slash is dropped from the rendered script path? The goal is to render the following:
<script src='static/js/search.js'></script>
I'd be very grateful for any insights others can offer on this question!
I was having a similar issue with loading a css file that was on a custom static path.
One fix could be to change the ROOT_DIRECTORY of the application, but this didn't work for my application as I only need to change the static path.
I used a combination of static_folder and static_url_path:
STATIC_URL_PATH = '/your/custom/path/static' # Where the css is stored
STATIC_FOLDER = 'your/custom/path/static'
app = Flask(__name__, static_folder=STATIC_FOLDER,
static_url_path=STATIC_URL_PATH)
As you can see, the main difference is the leading / in the beginning, but this made the app to be able to find the css.

Using CSS Templates with Flask: Overriding "static system"

I'm just starting out with Flask, and I was wondering what the best method for
dealing with how flask deals with static files when trying to use a premade CSS template.
Basically, I have downloaded a CSS template that I liked off the internet, but when if I simply drag the files into my flask application folder the CSS, JS, and image files do not work since they are not located in the static folder.
But if I move all the static files into the static folder, then I have to go through all the code and change the link urls, which is very time consuming.
The CSS Template I am using has an index.html that uses links like
<link rel = "stylesheet" href = "css/style.css" >
I have set both the static_folder = ""
and the static_url_path = "" in my flask app and I have moved the css, js, and image folders from the downloaded template into the base folder for the application, but the links are still not working.
Is there a better way to deal with using premade CSS templates with flask? Can I override the need to put css and js and image files in the static folder somehow? Thanks for your help!
(Sorry for opening this old post, but I'm on a badge hunt :])
There are several possible solutions, but the one I would recommend is to move the file style.css to folder <server_root>/static/css/.
Then create the flask app like app = Flask(__name__, static_url_path=''), what means that it still serves static files from the static/ folder, but on path / (so <server_root>/static/css/style.css is served on /css/style.css).
With this setup, your links href="/css/style.css" will work.
However, it's strongly recommended to use flask.url_for('endpoint', param='value') instead of /endpoint/url/value both in code and templates (surrounded with {{ ... }}) for all URLs - static files ('static', filename='css/style.css') and your own endpoints. So if your endpoint looks like this,
#app.route('/some/path/<variable>')
def some_endpoint(variable):
# do something and return response...
... you can use url_for('some_endpoint, variable='something') no matter what the actual URL (/some/path/something/ in this case) is. (Tested python 3.6.7; flask 1.0.2)

Flask-Assets or Bundles are not resovling the correct URL when multiple Blueprints are used

I'm having an issue that I really hope someone can keep me from banging my head against the table about.
I have a Flask app with multiple Blueprints that each have their own static and templates directories.
When only one Blueprint is registered and utilized the assets URL for bundled CSS and Javascript are both correctly resolved
However, when two or more Blueprints are resolved only the URLs for the assets in the last registered Blueprint resolve correctly (everything else seems to think that it's assets should be served out of the root static directory)
I'm getting incorrect asset URLs when templates from the Blueprints resolve the asset URLs. The URLs resolved are built incorrectly as if they should be served from a root static directory (which I would still like the overall app to have a static directory so that 400 and 500 errors can render pages using that):
http://{host}/static/public_css_app (404)
When it should be:
http://{host}/mod_core/static/public_css_app (200)
Here is part of the initiali(s)zation of the app
app = Flask(__name__)
# Import a module / component using its blueprint handler variable
# NOTE: The order that the Blueprints are imported in seems to relate to which one actual gets it's asset URLs rendered correctly. The last blueprint imported is the only one that works.
from appy.modules.core.controllers import mod_core
from appy.modules.auth.controllers import mod_auth
# Register blueprint(s)
appy.register_blueprint(mod_core)
appy.register_blueprint(mod_auth)
# Tried this method of setup and didn't do anything differently
# from flask.ext.assets import Bundle, Environment
# assets = Environment()
# assets.init_app(application)
Here's an example of one the Blueprint registrations inside of "{blueprint folder}/views.py":
One Blueprint's view:
mod_core = Blueprint('mod_core', __name__, url_prefix='/c', template_folder='templates', static_folder='static', static_url_path='/modules/core/static')
from . import bundles
Another Blueprint's view:
mod_auth = Blueprint('mod_auth', __name__, url_prefix='/auth', template_folder='templates', static_folder='static', static_url_path='/modules/auth/static')
from . import bundles
Here's an example of a bundles.py file (e.g. "/{blueprint_directory}/bundles.py"):
from appy import app
from flask import Flask
from flask.ext.assets import Bundle, Environment
bundles = {
'public_js_app: Bundle(
'mod_core/app/js/jquery.js',
'mod_core/app/js/app.js',
output='mod_core/b/app.js',
filters='rjsmin'),
'public_css_app': Bundle(
'mod_core/app/css/style.css',
'mod_core/app/font-awesome/css/font-awesome.css',
output='mod_core/b/app.css',
filters='cssmin'),
}
assets = Environment(app)
assets.register(bundles)
The templates are technically working and static files using url_for are working just fine, it's only the assets resolution that's not working.
{% assets "public_css_app" %}
<link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css" />
{% endassets %}
Any help would be greatly appreciated as I'm new to Flask.

Cannot show image from STATIC_FOLDER in Flask template

I set folder for static files like this
app.config['STATIC_FOLDER'] = 'tmp'
In template i use img tag to show an image stored in /tmp:
<img src='IKE2low.jpg' width="200" height="85">
In firebug i see 404 error instead of image. Please tell me what did i do wrong?
Thanks in advance.
I'm not sure what is this STATIC_FOLDER configuration item you are using. Where did you find it?
There are actually two arguments to the Flask class constructor that govern the configuration of static files:
static_folder: defaults to "static". This is the prefix that you have to use in URLs to access the static files.
static_url_path: this is the disk location of the static folder. By default this value is the same as the static_folder setting.
For example, if you use this configuration:
from flask import Flask
app = Flask(__name__, static_url_path = "/tmp", static_folder = "tmp")
Then you can access your images as follows:
<img src='/tmp/IKE2low.jpg' width="200" height="85">
You can also remove the need to have a prefix in the URLs as follows:
from flask import Flask
app = Flask(__name__, static_url_path = "", static_folder = "tmp")
And then you can access your images as:
<img src='/IKE2low.jpg' width="200" height="85">
Note that you still need to have a root /.
But the best way to do this is to not reference image paths explicitly and instead use url_for to generate the correct URLs. If you are using Jinja2 templates that would be:
<img src="{{ url_for('static', filename = 'IKE2low.jpg') }}" width="200" height="85">
This last expression would work regardless of where and how the static files are configured.

Python template help

I'm using App Engine's web-app templating system (similar if not identical to django)
Normally I render templates from my static directory /templates/ as
follows in my main handler:
dirname = os.path.dirname(__file__)
template_file = os.path.join(dirname, os.path.join('templates', template_name))
output = template.render(template_file,template_values)
self.response.out.write(output)
For my current app, I want to render a template contained in a
string. I'm using the following code:
t = template.Template(template_string)
c = template.Context(template_values)
output = t.render(c)
self.response.out.write(output)
It renders the template, but not the "include" tags contained within
the string. For example, the string template
"hello world {% include 'helloworld.html' %}"
renders "hello world" but does not render the contents of
'helloworld.html'.
I'm guessing that this has something to do with the string not being
in the same directory as 'helloworld.html', but I'm not sure how to
specify that the include tags should look in '/templates/*'
Any help would be appreciated,
Arjun
The webapp framework uses Django 0.9.6 templates. If you're loading templates from a string as you describe above, you need to configure the template loader so it can find dependencies loaded from files. Here's how webapp configures them.
{% include 'dirname/helloworld.html' %}
should work!

Categories