I have a single page app (SPA) that I am running off of Google App Engine (GAE). GAE does three things:
Serve the index.html file
Serve the static files (JS, CSS, etc.)
Serve the dynamic files (images, text, etc. via REST)
I use the following app.yaml configuration.
handlers:
- url: /app
static_dir: app
- url: /.*
script: main.app
My understanding is that this should match any requests going to the /app folder, which would serve my static files. All the REST services and the main index page would then be caught by the /.* and processed by main.py
However, I see the following behavior:
If I remove the /app handler, I can successfully serve the index.html (via Jinja templating) and the REST services (such as localhost/subjects/). However, I cannot see the static files (as expected).
If I add the /app handler, the index.html file does not serve and gives an "Internal Server Error" IOError(errno.EACCES, 'file not accessible', filename). However, when I request a static file such as: "localhost/app/app.js", this succeeds.
Is there something that I am missing here? I do not understand why the two would conflict.
Thank you!
Notes:
Google App Engine 1.8.9, Python 2.7, Developing locally
EDIT:
Here is the Python code I am using to serve the page
path = os.path.join(os.path.dirname(__file__), 'app')
jinja_environment = jinja2.Environment(loader=jinja2.FileSystemLoader(path))
class MainHandler(webapp2.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/html'
template_values = {}
template = jinja_environment.get_template('index.html')
self.response.out.write(template.render(template_values))
My directory structure is as follows:
/
main.py
app.yaml & etc.
app
index.html
app.js
Module A
moduleA.tpl.html
moduleA.js
Edit 2:
I moved index.html to the root directory (/), and then used the following python code:
path = os.path.dirname(__file__)
jinja_environment = jinja2.Environment(loader=jinja2.FileSystemLoader(path))
It (very interestingly) appears that the "double mapping" of the index.html to the Jinja template and to the static directory files caused a problem. I wonder what is the best practice way to do this. I use Jinja for one reason: adding the (GAE generated) login/logout link to the index.html file. Other than that, there is no reason for using it.
Python is by default unable to access files or directories marked as static in App Engine. You can add application_readable: true to your handler mapping to enable this.
Another solution would be to move the index.html away from the static folder since it in fact isn't a static file but instead is a jinja template for Python.
Related question: Read a file on App Engine with Python?
Documentation for application_readable
application_readable
Optional. By default, files declared in static file handlers are uploaded as static data and are only served to end users, they cannot be read by an application. If this field is set to true, the files are also uploaded as code data so your application can read them. Both uploads are charged against your code and static data storage resource quotas.
Ok it might explains scenario #2, how do you access the index.html? If you add /app to the handler and your request url is something like /app/index.html it will serve from the static dir.
On your scenario seems your url does not contain /app, therefore it goes to the 2nd handler rule which is going to main.app.
However since you put the html inside the /app, appengine treats it as static file. If you want to serve the file from jinja template, you should not put it in static_dir
Jinja templates (or Django, Mako etc) has no requirement to be sat in a publicly accessible folder. They are always called via a handler in the application and compiled in the application before serving to the user.
It is common practice to put these in a /templates directory. There is no reference to this directory in app.yaml, it is purely used internally to serve the templates. Check out some of the boilerplate apps in github (search 'gae boilerplate'). This one is minimal and uses Jinja so may be a good example for you https://github.com/SoulAuctioneer/notvanillae
Related
I have a directory structure that looks like this:
project/
index/
about.html
index.html
forum.html
profile.html
settings.html
apple-touch-icon.png
static/
main.css
forum.css
main.js
forum.js
load-image.min.js
server.py
metaclass.py
mailing.py
errors.log
I'd like to be able to make cherrypy serve all of these files from index/. However, I also want about.html, index.html, forum.html, profile.html, etc. to be accessible via /about, /, /forum, /profile, etc., so this is not the same as just simple static file serving. Also, I want to have some custom methods, like /login, which needs a GET and POST, and pre-templated user profile pages. How can this be done?
Cherrypy is going to recursively serve the files in the index folder. What you are trying to do has more to do with the url path.
In your server.py you can attach the handler for /about.html to achieve what you want.
#cherrypy.expose
def about_html(self):
return open('/index/about.html')
hope this helps!
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)
I'm testing one and the same application both on the default Django server and on Apache and I see a lot of big differences. I managed to resolve some of them, but at this moment I'm unable to resolve a major difference. So, in project settings.py file I have this code:
MODULES_DIR = BASE_DIR + '/system/modules/'
for item in os.listdir(MODULES_DIR):
stat = os.path.join(MODULES_DIR, item + '/static')
if os.path.isdir(os.path.join(MODULES_DIR, item)):
INSTALLED_APPS += ('system.modules.%s' % item, )
STATICFILES_DIR += (stat, )
APPS_DIR = true
This code is supposed to populate INSTALLED_APPS dynamically, based on the contents of BASE_DIR + '/system/modules/' folder. In other words, if there is a folder inside /modules, this folder becomes an application. Likewise, I build dynamically STATICFILES_DIR - in this case it is supposed, that every single folder/application (which is inside /modules folder) has a /static folder with static contents - js, css etc. For example, it may be such a construct:
\modules
\DefaultModule
__init__.py
urls.py
views.py
\static
test.js
\templates
DefaultModule.html
And DefaultModule.html in this example loads static files like this:
<html>
<head>
{% load static from staticfiles %}
<script type="text/javascript" src="{% static "test.js" %}"></script>
It is rather interesting, but on default Django server this logic works perfectly, so that when I go in my browser to localhost/DefaultModule/, I see a template DefaultModule.html loaded and I see test.js file loaded from http://localhost/DefaultModule/static/. However, on Apache the template is rendered too, but the test.js file is loaded from http://localhost/static/ what eventually results in a 404 NOT FOUND error. So, for some reason Apache server does not take into account STATICFILES_DIR. And yes I checked its (I mean STATICFILES_DIR) contents and it is the same. In both cases STATICFILES_DIR contains modules/DefaultModule/static/, but on Apache it is ignored for some reason. Hope someone can help. Thanks!
I think you should read the Django docs on static files. Looks like you're falling into the simple and old Django Static File Hosting an Apache
Check it out and let us know.
I am developing a SAAS application using Flask and I want users to be able to create/upload/use their own custom templates. This is how my directory structure is right now :
/flaskapp
/application.py
/static
/style.css
/templates (site & admin templates goes here)
/hello.html
/userdata
/user1
/template1
hello.html
/template2
hello.html
/user2
/template1
hello.html
/template2
hello.html
I am able to serve user specified templates using a solution found through this stackoverflow question : How to dynamically select template directory to be used in flask? but how do I serve static files from template directory. Instead of serving static files from /flaskapp/static/ I want to serve static files using /flaskapp/userdata/<user>/<current-template>/static/ directory where and will be determined dynamically at run time. How to do this?
Presumably you're using a web server in front of Flask. One way to solve this (which I generally use when using Apache + mod_wsgi for custom WSGI apps) is to just serve the directory straight from disk via the web server. On Apache, I just use an Alias directive for this.
If you want to vary the file served under a given URL per user, you would have to pipe the file through Flask. You'd have to figure out how to properly route the request; after that, you might be able to use wsgi.file_wrapper to send the correct file (though I'm not sure how you'd get at this through Flask).
This is what I use to serve files from whatever system directory.
The app searches a specific directory for a specific requested file:
when someone accesses http:/host/where/to/serve/files/example_file.txt the application will try to return example_file.txt as attachment, if the file doesn't exist it will return a 404.
You can adjust this and build the DIRECTORY_TO_SERVE_PATH with your user variables.
You should also validate the files to serve if you have any restrictions because this returns any file that exists in the path.
import os
from flask import abort, send_from_directory
DIRECTORY_TO_SERVE_PATH = '/where/files/are/in/disk/'
#app.route('/where/to/serve/files/<path:filename>')
def download_file(filename):
if os.path.exists(DIRECTORY_TO_SERVE_PATH + filename):
return send_from_directory(DIRECTORY_TO_SERVE_PATH, filename, as_attachment=True)
else:
abort(404)
I have a Pyramid app that I've been developing and viewing locally through http://localhost:6543, which is what the paster docs suggest. There are various assets, for example .css files, available in a static directory, which I have made available through config.add_static_view('static','static') When I view it through localhost everything works fine, my .css files are loaded, and all is well. However, when I view the app through my computer's hostname/IP address, the static assets aren't loaded.
A freshly-installed Pyramid paster scaffold displays the same behavior. I've gone through and followed the Pyramid narrative documentation through the step called viewing the application, and everything works as is described. But change the URL in the location bar from http://localhost:6543 to http://my.host.name:6543 and the style sheets don't get loaded.
The assets are available; type http://my.host.name:6543/static/pylons.css on a freshly-created Pyramid paster scaffold and you can read the contents of the css, but it's not loaded when the root page is loaded. Firebug indicates that those resources are requested, but never received.
What's going on here, and how can I make sure that my static assets are loaded when I request them through something other than localhost?
Edit to add some code. This is from the Pyramid starter paster scaffold, which I haven't modified and which exhibits the same behavior my application does; it can be considered a minimal example.
from package's __init__.py:
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(settings=settings)
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home', '/')
config.scan()
return config.make_wsgi_app()
complete views.py:
from pyramid.view import view_config
#view_config(route_name='home', renderer='templates/mytemplate.pt')
def my_view(request):
return {'project':'TestProject'}
relevant line from templates/mytemplate.pt:
<link rel="stylesheet" href="/static/pylons.css" type="text/css" media="screen"
charset="utf-8" />
Where static/pylons.css is in the static directory under the root.
Again, opening http://0.0.0.0:6543 in the browser (FF10) works fine, http://my.host.name:6543 displays the page without style information; but http://my.host.name:6543/static/pylons.css gives me the style sheet text.
Without being able to look at your code it's a bit hard to answer this question. My guess based on a similar experience is that you might not be using the static_url method inside your templates. Below is another way to reference static assets in your templates:
<link rel="stylesheet" href="${request.static_url('app:static/css/app.css')}">
Are you using that? Using anything else will certainly cause the behaviour you are seeing.
There seems to be a problem if the application is installed to a path that is not the document route. You can see this discussed here.