Python-Eve serve images - python

Good day! I have a REST API, which based on Python-Eve framework. I need to serve images, like simple Flask application (the reason is that doesn't need to run two servers). I'm move REST API to 'api' prefix and make simple function for serve static files:
from flask import send_from_directory
import eve
app = eve.Eve()
#app.route('/images/<path:path>')
def serve_static(path):
return send_from_directory('images', path)
if __name__ == '__main__':
app.run()
But it doesn't work and give me 404 error. When I replace send_from_directory for something simpler, like return "Hello" it works ok. Any idea how to implement it correctly? Thanks in advance.

because of flask don't where is 'image' folder, you should specify it clearly. e.g. from the configuration.
official doc: send_from_directory
the official example:
#app.route('/uploads/<path:filename>')
def download_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],
filename, as_attachment=True)

I solve this problem wirh send_file function from flask package.
This is my code:
import os
from flask import send_file, abort, request
import eve
from settings import BASE_DIR
app = eve.Eve()
#app.route('/images')
def serve_images():
if request.args.get('path'):
path = os.path.join(BASE_DIR, 'app', 'images', request.args.get('path'))
else:
abort(403)
return send_file(path)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
This aproach is very useful for me, because I can send files, which left outside flask application's folder.

Related

Reference app location in a Flask Blueprint

I have a blueprint where I have an upload form. I am attempting to save the files, but I cannot figure out how to reference the correct directory. When I use app.instance_path, it gives the error message:
NameError: name 'app' is not defined
How can I declare the correct folder location to store the file from within a Flask Blueprint?
Here is my init.py file:
# External libraries
from flask import Flask
# Import the resources
from . import students
def create_app(configfile=None):
app = Flask(__name__)
app.register_blueprint(students.bp)
return app
Here is my students.py blueprint file:
import os
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.utils import secure_filename
from SIMPLE.forms import ImportStudentsForm
bp = Blueprint('students', __name__, url_prefix='/students')
#bp.route('/import', methods=('GET', 'POST'))
def import_students():
# Load the register form
form = ImportStudentsForm(request.form)
if form.validate_on_submit():
f = request.files['file']
filename = secure_filename(f.filename)
f.save(os.path.join(
app.instance_path, 'uploads', filename
))
# Flash success
flash('Sucessfully registered.', 'success')
return render_template('students/batch_import_students.html', form=form)
Any guidance would be much appreciated. Thanks.
You get the error because you have not imported app in students.py. You could import it (and run the risk of circular imports) but a more elegant way is to get the app instance through flask.current_app.
Add from flask import current_app to the top of students.py and replace app.instance_path with current_app.instance_path.
Try this ::
from flask import current_app
AND
f.save(os.path.join(current_app.instance_path, 'uploads', filename))
You are using application factory. So there is no 'app' object here. Whatever app flask is currently using can be fetched with current_app and then you can work with it.
Also, if the instance_path doesn't work, try current_app.root_path

Flask Configuration for Sharing Resources Across Blueprints

I have an extremely large application that is factored into many blueprints. I need to configure the entire application to save files into a directory when a user uploads files. So I am setting ["SESSION_FILE_DIR"] = 'C:/some/path' to the path where files would live after upload in the app.py file.
The documentation reads that configuration settings are shared across the blueprints. I'm experiencing one issue that is not working and below is a stripped down example to replicate my problem to support my question. So, to debug I've created an extremely simple version just printing the path to screen to see what is happening.
If I head over to the URL .../print_dir then the ["SESSION_FILE_DIR"] = 'C:/some/path' is printed to screen. However, if I head over to the URL defined within the blueprint .../new then I am given an error of NameError: name 'server' is not defined.
How can I configure my app such that the same ["SESSION_FILE_DIR"] can be used across blueprints?
The contents of my main app.py are as follows:
from flask import Flask, render_template, request, session
from flask_session import Session
import tempfile
server = Flask(__name__)
server.config["SESSION_PERMANENT"] = False
server.config["SESSION_TYPE"] = "filesystem"
server.config["SESSION_FILE_DIR"] = 'C:/some/path'
server.secret_key = 'abcdefg'
### Import and Register Blueprints
from tools.routes import my_bp
server.register_blueprint(my_bp)
#server.route('/')
def homepage():
return "Hello"
#server.route('/print_dir')
def homepage2():
return server.config["SESSION_FILE_DIR"]
if __name__ == '__main__':
server.run(debug=True)
and now suppose the blueprint lives in a subdirectory called tools and contains the following:
from flask import Flask, render_template, request, session, Blueprint
from flask_session import Session
my_bp = Blueprint("my_bp", __name__)
#my_bp.route('/new', methods=['POST', 'GET'])
def path():
path = server.config["SESSION_FILE_DIR"]
return path
To access the app.config from within a Blueprint, you can import current_app from flask.
Example:
# ./app.py
from flask import Flask
from views.blueprint import bp
app = Flask(__name__)
# Set the config you want:
app.config['IMPORTANT_DIRECTORY'] = '~/path/to/important/directory'
# Register your blueprint
app.register_blueprint(bp)
# ./views/blueprint.py
from flask import Blueprint, current_app
bp = Blueprint('bp', __name__)
#bp.route('/path')
def get_path():
# access the config
path = current_app.config['IMPORTANT_DIRECTORY']
return path
You can find more info in the api docs for flask.

Why does my view function 404?

Directory Structure:
__init__:
from flask import flask
app = Flask(__name__)
if __name__ == '__main__'
app.run()
Views:
from app import app
#app.route('/')
def hello_world():
return 'Hello World!'
I hope someone can explain what I am doing wrong here -
I guess I'm not understanding how to properly import app. This results in a 404. However when views is moved back to __init__ everything works properly.
You need to explicitly import your views module in your __init__:
from flask import flask
app = Flask(__name__)
from . import views
Without importing the module, the view registrations are never made.
Do keep the script portion outside of your package. Add a separate file in Final_app (so outside the app directory) that runs your development server; say run.py:
def main():
from app import app
app.run()
if __name__ == '__main__'
main()

How to serve static file in Flask

I have a google verification file that I need to have in my root directory. How do I serve it and set up the route correctly in my app.py file? I thought just having it in the static directory would do the trick.
In my app.py file:
import requests; requests = requests.session()
from flask import (
Flask,
g,
session,
request,
render_template,
abort,
json,
jsonify,
make_response
)
from jinja2 import TemplateNotFound
app = Flask(__name__)
...
#app.route('/ping')
def ping():
return "OK"
"""
Catch-All Route
first looks for templates in /templates/pages
then looks in /templates
finally renders 404.html with 404 status
"""
#app.route('/', defaults={'path': 'index'})
#app.route('/<path:path>')
def show_page(path):
if session.get('tracking_url'):
session['session_url'] = False
templates = [t.format(path=path) for t in 'pages/{path}.html', '{path}.html']
g.path = path
try:
return render_template(templates, **site_variables(path))
except TemplateNotFound:
return render_template('404.html', **site_variables(path)), 404
application = app
if __name__ == '__main__':
app.run('0.0.0.0', debug=True)
I've tried adding this but it didn't work:
#app.route('/myfile.html')
def myfile():
return send_from_directory('/static', 'myfile.html')
For a one-off file that Google looks for in the root, I'd just add a specific route:
from flask import send_from_directory
#app.route('/foobar_baz')
def google_check():
return send_from_directory(app.static_folder, 'foobar_baz')
You are free to add in test in show_page(path) to try and serve path with send_from_directory() before you test for a template, of course; the second filename argument can take relative paths.

Split Python Flask app into multiple files

I'm having trouble understanding how to split a flask app into multiple files.
I'm creating a web service and I want to split the api's into different files (AccountAPI.py, UploadAPI.py, ...), just so I don't have one huge python file.
I've read that you can do this with Blueprints, but I'm not entirely sure that route is the right one for me.
Ultimately I want to run one Main python file and include other files so that when it runs, they are considered one big file.
For example if I have Main.py and AccountAPI.py I want to be able to do this:
Main.py:
from flask import Flask
import AccountAPI
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
AccountAPI.py:
#app.route("/account")
def accountList():
return "list of accounts"
I know with this example it obviously won't work, but is it possible to do something like that?
Thanks
Yes, Blueprints are the right way to do it. What you are trying to do can be achieved like this:
Main.py
from flask import Flask
from AccountAPI import account_api
app = Flask(__name__)
app.register_blueprint(account_api)
#app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
AccountAPI.py
from flask import Blueprint
account_api = Blueprint('account_api', __name__)
#account_api.route("/account")
def accountList():
return "list of accounts"
If this is an option, you might consider using different URL prefixes for the different APIs/Blueprints in order to cleanly separate them. This can be done with a slight modification to the above register_blueprint call:
app.register_blueprint(account_api, url_prefix='/accounts')
For further documentation, you may also have a look at the official docs.
Using Blueprint you can add your routes in the routes directory.
Structure
app.py
routes
__init__.py
index.py
users.py
__init__.py
from flask import Blueprint
routes = Blueprint('routes', __name__)
from .index import *
from .users import *
index.py
from flask import render_template
from . import routes
#routes.route('/')
def index():
return render_template('index.html')
users.py
from flask import render_template
from . import routes
#routes.route('/users')
def users():
return render_template('users.html')
app.py
from routes import *
app.register_blueprint(routes)
If you want to add a new route file, say accounts.py, you just need to create the file accounts.py in the routes directory, just like index.py and users.py, then import it in the routes.__init__.py file
from .accounts import *
If you are using blueprints and want to route / redirect to a url of your blueprint inside a template you are using you need to use the correct url_for statement.
In your case if you would like to open the url account of your blueprint you have to state it like this in your template:
href="{{ url_for('account_api.account') }}"
and for the main app it would look like this:
redirect(url_for('account_api.account'))
Otherwise the werkzeug library will throw an error.
One another way to do this can be with lazy loading, where you would explicitly attach view functions on need basis.

Categories