Equivalent to wildcards in Flask's url_for? - python

I am developing a Flask application, and have it set so that my app's CSS file is generated with a random name via this build script:
rm style/main-*.css;
KEY=`tr -dc A-Za-z0-9 </dev/urandom | head -c 6`;
postcss build ./style/src/main.css -o ./style/main-$KEY.css
This is mostly for cache-related reasons; but it regardless results in a name with the wildcard pattern main-*.css
Since this name is randomized every build, I do not want to edit the template I include it in. Unfortunately, wildcards do not appear to work in Flask, as this:
<link rel='stylesheet" href='{{ url_for('static', filename='style/main-*.css') }}'>
… does nothing (except, of course, yield a 404).
Is there a way to do this kind of behavior via Flask? Thank you.

You can write your own function and add it to the jinja2 environment as a global function.
import os
from glob import glob
from flask import current_app
def glob_assets(target):
root = current_app.static_folder
return [f[len(root)+1:] for f in glob(os.path.join(root, target))]
app.jinja_env.globals.update(get_assets=glob_assets)
Then you can add the stylesheets to the template within one iteration.
{% for css in get_assets('style/main-*.css') -%}
<link rel="stylesheet" href="{{ url_for('static', filename=css) }}">
{% endfor -%}

Related

FastAPI serving static files through symlinks

I have mounted the static directory in my FastAPI app using the following code:
from fastapi.staticfiles import StaticFiles
app = FastAPI(
title="Title of the Application",
description="Over all description of the application")
app.mount("/public", StaticFiles(directory='public'), name='public')
If I have a symlink pointing to a path outside the app folder, e.g.
/home/xyz/app/main.py
/home/xyz/app/index.html
/home/xyz/app/public/data -> /home/xyz/static/whatever.tgz
The FastAPI application can recognize the URL xyz.com/public/index.html, but it can't recognize xyz.com/public/data.
Is this doable? Unfortunately, I cannot use FileResponse due to the blob size being too large. I want to return the file with a simple link somehow.
It is doable, as long as you mount a StaticFiles instance on that specific path as well. For example:
app.mount("/public", StaticFiles(directory="public"), name="public")
app.mount("/publicsym", StaticFiles(directory="public/data"), name="publicsym")
Then in your Jinja2 template you can requesst the files as below:
<link href="{{ url_for('public', path='/styles.css') }}" rel="stylesheet">
<img src="{{ url_for('publicsym', path='/image.png')}}" width="50%">
or, as per your given example (if there is a "static" directory including a "whatever.tgz" file):
{{ url_for('publicsym', path='static/whatever.tgz')}}

jQuery doesn't work when delivered with flask but works otherwise [duplicate]

This question already has answers here:
How to serve static files in Flask
(24 answers)
Link to Flask static files with url_for
(2 answers)
Closed 4 years ago.
I'm pretty new to python, even less experienced with flask, and I cannot figure out this issue. I have the following simple web page with jQuery functionality that works great when I double click the file and open it in a browser:
<!DOCTYPE html>
<html>
<head>
<script src="jquery-3.3.1.js"></script>
</head>
<body>
<script type="text/javascript">
$(document).ready(function() {
$("#updateBtn").on("click", function() {
text = "<h2>The div has been updated!</h2>";
$("#jQuery_div").html(text);
});
});
</script>
<div>
<h1>This is a non-jQuery div</h1>
</div>
<div id="jQuery_div">
<h2>This div should update with jQuery</h2>
</div>
<button id="updateBtn">update</button>
</body>
</html>
However, when flask delivers the web page on localhost:5000, the jQuery functionality is no longer present. My python is as follows:
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def render():
return render_template("jquery_test.html")
if __name__ == "__main__":
app.run(port=5000, debug=True)
My app's file tree is:
/AJAX_practice
ajax_practice.py
/templates
jquery-3.3.1.js
jquery_test.html
I was trying to follow this tutorial when I couldn't get the "echo" button to work. In my efforts to debug, I have slowly chipped away and drastically simplified the program to the above code to see why I cannot get my jQuery to work through flask. I am still at a loss. I am running the flask app by pressing F5 in IDLE, with no errors in Python 2.7.13 Shell, and the Terminal (from which I started IDLE with $ sudo idle) showing:
my ip - - [date and time] "GET / HTTP/1.1" 200 -
my ip - - [date and time] "GET /jquery-3.3.1.js HTTP/1.1" 404 -
From this, my best guess is that flask cannot find the jquery.3.3.1.js file, though I have tried putting it everywhere in the file tree with no luck. I cannot use the script src to https for jQuery dependencies, as my server will eventually be on a non-internet connected LAN. Am I on the right track? If so, how does flask find and/or navigate jQuery dependencies? Can anyone point me towards some documentation that might help my fundamental understanding of this issue?
Any help on this matter would be greatly appreciated.
You are trying to serve JavaScript file from templates folder. Add a static folder and use that to serve static content.
in your case create a directory structure like "static/js/jquery.min.js"
and then add script reference like this
<script src="{{url_for('static', filename='js/jquery.min.js')}}"></script>
See this :
http://exploreflask.com/en/latest/static.html
If you don't want to keep it in "static" folder and use another local directory you can use send_from_directory as shown in this example :
https://stackoverflow.com/a/20648053/2118215
This has always worked for me with Flask in the past:
<script src="{{ url_for('static', filename='jquery-3.3.1.js') }}"></script>
'static' is the name of the folder it's in (and the 'static' folder is in the root of my project). You can edit this to suit your preferred structure and naming, so change 'static' to 'templates' if that's where you'd rather keep your jquery file, although I would recommend keeping it in a separate folder from your HTML templates, purely in the interests of keeping your project well organised.
I believe the path to jquery should be /templates/jquery-3.3.1.js
On me flask server when i serve jquery it has the full path from the home directory: /static/js/jquery.min.js

Flask : how to "fix" the static directory

I have this issue with Flask when i try running this code with Flask :
#app.route('/viz')
def root():
return render_template('page.html')
The file 'page.html' is in the templates folder, and contains some lines like this :
<link rel="stylesheet" href="static/main.css">
But when I execute my program, the file doen't look for main.css in the static directory, but in the viz/static direcory ( /viz is the route of the view).
How can I solve this issue ?
Thanks and sorry for my english.
You need a leading slash to tell the browser to use an absolute path.
<link rel="stylesheet" href="/static/main.css">
You need to place the static folder within the package or next to your module. See the Flask Quickstart documentation for more details. Without knowing more about your structure, my guess is your tree should look something like this:
- top level directory (e.g. your package "root")
| app.py
| static/
| viz/

Custom filter only works for certain name jinja2

so I tried using custom filter in jinja2 and flask
so here is the problem:
this is the html file
<h1>Hello, {{ user.nickname | reverse }}!</h1>
and this is the py file
from app import app
def reverse_filter(s):
return s[::-1]
app.jinja_env.filters['reverse'] = reverse_filter
the code above is working fine. But now if I change the name 'reverse' into e.g.'mouse'
<h1>Hello, {{ user.nickname | mouse }}!</h1>
then the new py
from app import app
def mouse_filter(s):
return s[::-1]
app.jinja_env.filters['mouse'] = mouse_filter
it will give me jinja2.exceptions.TemplateAssertionError: no filter named 'mouse'
It is really weird... did I do something wrong?
I found out the mistake,
I put my filter in separate file (jinja_custom_filter.py) from my __init__.py file so I need to put
from app import jinja_custom_filter.py in my init file to be able to use it.
Still don't really understand why I need to put every reference file in __init__.py though

How to build an offline web app using Flask?

I'm prototyping an idea for a website that will use the HTML5 offline application cache for certain purposes. The website will be built with Python and Flask and that's where my main problem comes from: I'm working with those two for the first time, so I'm having a hard time getting the manifest file to work as expected.
The issue is that I'm getting 404's from the static files included in the manifest file. The manifest itself seems to be downloaded correctly, but the files that it points to are not. This is what is spit out in the console when loading the page:
Creating Application Cache with manifest http://127.0.0.1:5000/static/manifest.appcache offline-app:1
Application Cache Checking event offline-app:1
Application Cache Downloading event offline-app:1
Application Cache Progress event (0 of 2) http://127.0.0.1:5000/style.css offline-app:1
Application Cache Error event: Resource fetch failed (404) http://127.0.0.1:5000/style.css
The error is in the last line.
When the appcache fails even once, it stops the process completely and the offline cache doesn't work.
This is how my files are structured:
sandbox
offline-app
offline-app.py
static
manifest.appcache
script.js
style.css
templates
offline-app.html
This is the content of offline-app.py:
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/offline-app')
def offline_app():
return render_template('offline-app.html')
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
This is what I have in offline-app.html:
<!DOCTYPE html>
<html manifest="{{ url_for('static', filename='manifest.appcache') }}">
<head>
<title>Offline App Sandbox - main page</title>
</head>
<body>
<h1>Welcome to the main page for the Offline App Sandbox!</h1>
<p>Some placeholder text</p>
</body>
</html>
This is my manifest.appcache file:
CACHE MANIFEST
/style.css
/script.js
I've tried having the manifest file in all different ways I could think of:
CACHE MANIFEST
/static/style.css
/static/script.js
or
CACHE MANIFEST
/offline-app/static/style.css
/offline-app/static/script.js
None of these worked. The same error was returned every time.
I'm certain the issue here is how the server is serving up the files listed in the manifest. Those files are probably being looked up in the wrong place, I guess. I either should place them somewhere else or I need something different in the cache manifest, but I have no idea what. I couldn't find anything online about having HTML5 offline applications with Flask.
Is anyone able to help me out?
I would have thought this one would work:
CACHE MANIFEST
/static/style.css
/static/script.js
But in any case, you should not hardcode the URLs for your static files. It's best to serve the manifest as a template (moved to the "templates" folder) so that you can use url_for to generate the path to the static files, something like this:
CACHE MANIFEST
{{ url_for('static', filename='style.css') }}
{{ url_for('static', filename='script.js') }}
Then in your HTML template you would have a reference to a route instead of a static file:
<html manifest="{{ url_for('manifest') }}">
And finally, you would add a new view function that returns the manifest:
from flask import make_response
#app.route('/manifest')
def manifest():
res = make_response(render_template('manifest.appcache'), 200)
res.headers["Content-Type"] = "text/cache-manifest"
return res

Categories