I have a flask api which I have wrapped up in an object. Doing this has made unit testing a breeze, because I can instantiate the api with a variety of different settings depending on whether it is in production, test, or whatehaveyou.
I am now trying to extend the api a bit, and for that I'm using a blueprint. The problem is that I cannot figure out how to pass arguments to the blueprint. My routes require information like which database to access, and that information is not static. How can I pass this information into a blueprint? I have included code below as an example:
api.py:
class MyApi(object):
def __init__(self, databaseURI):
self.app = Flask(__name__)
self.app.register_blueprint(myblueprint)
blueprint.py
myblueprint= Blueprint('myblueprint', __name__)
#myblueprint.route('/route', methods=['GET'])
def route():
database = OpenDatabaseConnection(databaseURI)
There is a related question here:
How do I pass constructor arguments to a Flask Blueprint?
But the people who answer the question solve the op's use-case specific problem without actually answering the question of how to pass arbitrary arguments to a blueprint.
You could create the blueprint dynamically in a constructor function:
def construct_blueprint(database):
myblueprint = Blueprint('myblueprint', __name__)
#myblueprint.route('/route', methods=['GET'])
def route():
database = database
return(myblueprint)
Use Flask config system (app.config) to store all your config data, then from your Blueprint read your Application Context using current_app.
Store in app.config:
app.config[DATABASE_URI] = databaseURI
Read application context:
databaseURI = current_app.config[DATABASE_URI]
Sample code
main.py
from flask import Flask
from blueprint import myblueprint
app = Flask(__name__)
app.register_blueprint(myblueprint)
app.config[DATABASE_URI] = databaseURI
blueprint.py
from flask import current_app
myblueprint= Blueprint('myblueprint', __name__)
#myblueprint.route('/route', methods=['GET'])
def route():
databaseURI = current_app.config[DATABASE_URI]
database = OpenDatabaseConnection(databaseURI)
This way it's possible to add a blueprint, passing parameters, inside an appfactory. Sometimes you'll need it and it's not described anywhere in the flask docs.
Assuming bp1.py, bp2.py are in the same folder as your appname
from appname import bp1, bp2
app.register_blueprint(bp1.bp)
# with parameters:
app.register_blueprint(bp2.construct_blueprint(arg1, arg2))
inside bp2.py:
def construct_blueprint(arg1, arg2):
bp = Blueprint("bp2", __name__)
#bp.route('/test')
def test():
ret = {'arg1': arg1, 'arg2': arg2}
return jsonify(ret)
return bp
I went about this in a slightly different way by creating a class in the helper blueprints python files. This way I can have one call to load the class and then pass the result to the blueprint function in the main python script. When I load the class I can pass any attributes I've configured. I like this approach as it keeps my code much cleaner.
The code I've put in here can also be found at https://github.com/dacoburn/example-flask-blueprints if you want to download it. I added comments to the github with more details of what is going on in the python files.
The folder structure I have is:
src
|___main.py
|___routes
|___index.py
|___example.py
|___templates
|___index.html
|___example.html
main.py
from flask import Flask
import os
from routes.index import Index
from routes.example import Example
app = Flask(__name__)
index = Index("Example User")
example = Example("Random Arg")
app.register_blueprint(index.index)
app.register_blueprint(example.example)
if __name__ == '__main__':
port = int(os.environ.get('APP_PORT', 5000))
app.run(host='0.0.0.0', port=port, debug=True)
index.py
from flask import render_template, Blueprint
class Index:
def __init__(self, username):
self.username = username
self.index = self.create_index()
def create_index(self):
index_page = Blueprint("index", __name__)
#index_page.route("/", methods=['GET'])
def index():
return render_template("index.html", username=self.username)
return index_page
example.py
from flask import render_template, Blueprint
class Example:
def __init__(self, arg):
self.arg = arg
self.example = self.create_example()
def create_example(self):
example_page = Blueprint("example", __name__)
#example_page.route("/<username>", methods=['GET'])
def example(username):
return render_template("example.html",
username=username,
arg=self.arg)
return example_page
index.html
<html>
<head>
<title>Example Page</title>
</head>
<body>
<p>Hello {{ username }}</p>
<br>
This page also has a redirect to another route:
<ul>
<li>Example Page</li>
</ul>
</body>
</html>
example.html
<html>
<head>
<title>Example Page 2</title>
</head>
<body>
<p>Hello {{ username }}</p>
<br>
Here is the random argument: {{ arg }}
<br>
<br>
This page also has a link to the main page:
<ul>
<li>Index Page</li>
</ul>
</body>
</html>
Related
How can I decouple this class? I would like to put the paths in another file, is it possible to move the routes in another file?
#api.route('/home', '/api/email')
class Server(Resource):
def create_server(app, oauth=None):
if not oauth:
oauth = default_provider(app)
app = prepare_app(app)
#app.before_request
def load_current_user():
user = User.query.get(1)
g.user = user
#app.route('/home')
def home():
return 'home'
#app.route('/oauth/authorize', methods=['GET', 'POST'])
#oauth.authorize_handler
def authorize(*args, **kwargs):
return True
Those
#app.route('/home') # and
#app.route('/oauth/authorize', methods=['GET', 'POST'])
have to be in another file.
My attempt was this, I tried to create a file for routers:
class Router():
def __init__(self, app, oauth):
self.app = app
self.oauth = oauth
#app.route('/home')
def home():
return 'home'
I'm getting this error:
NameError: name 'app' is not defined
Well, I see a package you can use for flask projects called Flask-Via [pypi], inspired by the Django URL configuration system and designed to add similar functionality to Flask applications that have grown beyond a simple single file application. The following example is given from the docs of this project:
from flask import Flask
from flask.ext.via import Via
from flask.ext.via.routers.default import Functional
app = Flask(__name__)
def foo(bar=None):
return 'Foo View!'
routes = [
Functional('/foo', foo),
Functional('/foo/<bar>', foo, endpoint='foo2'),
]
via = Via()
via.init_app(app, route_module='flask_via.examples.basic')
if __name__ == "__main__":
app.run(debug=True)
I think this is exactly what you want :) and you can move it to another python module for example called routers.py.
I am learning flask with blueprints and I have the following code example:
from flask import Flask, render_template, Blueprint, g
bp = Blueprint('language', __name__, url_prefix='/<int:language_id>', static_folder='static')
app = Flask(__name__)
#bp.url_defaults
def add_language_id(endpoint, values):
if 'language_id' in g:
values.setdefault('language_id', g.site_id)
else:
g.language_id = 1 # English
#bp.url_value_preprocessor
def pull_language_id(endpoint, values):
g.language_id = values.pop('language_id')
#bp.route('/')
def index():
return render_template('main.html')
#bp.route('/lang')
def language():
return render_template('lang.html')
app.register_blueprint(bp)
if __name__ == '__main__':
app.run()
But I'm getting this error:
werkzeug.routing.BuildError: Could not build url for endpoint
'language' with values ['language_id']. Did you mean 'language.index'
instead?
Here is the HTML code:
<ul>
<li>
English
</li>
</ul>
How do I correct this error?
Looks like you are missing the Blueprint name (language) in your url link:
From here:
bp = Blueprint('language', __name__, url_prefix='/<int:language_id>', static_folder='static')
So your link will need to be appended:
English
I would also suggest naming it something different so it doesn't conflict with any other naming convention you might have.
So there's this flask app that I'm working on for this project and I need it to run in a loop at timed variables to check for the status of certain variables and then give a output accordingly. However, the problem I have is I need to render a template in Flask before the loop restarts. In the changelog on http://flask.pocoo.org/ it's indicated that it's possible to render templates without using the request context but I haven't seen any real examples of this. So is there a way to render templates in Flask without having to use the request context without getting any errors? Any help that can be given is appreciated.
UPDATE:
Here's the code I'm working with
from flask import Flask, render_template, request, flash, redirect, url_for
import flask
import time
from flask.ext.assets import Environment, Bundle
from flask_wtf import Form
from wtforms import TextField, TextAreaField, SubmitField
from wtforms.validators import InputRequired
CSRF_ENABLED = True
app = Flask(__name__)
app.secret_key = 'development key'
app = flask.Flask('my app')
assets = Environment(app)
assets.url = app.static_url_path
scss = Bundle('scss/app.scss', filters='scss', output='css/app.css')
assets.register('app_scss', scss)
#app.route('/')
def server_1():
r=1
g=2
b=3
i=g
if i == g:
with app.app_context():
print "Loading Template..."
rendered = flask.render_template('server_1.html', green=True)
print "Success! Template was loaded with green server status..."
time.sleep(5)
if __name__ == '__main__':
app.run(port=5000, debug=True)
You can do it by binding your application as the current application. Then you can use render_template() to render a template from your template directory, or render_template_string() to render directly from a template stored in a string:
import flask
app = flask.Flask('my app')
with app.app_context():
context = {'name': 'bob', 'age': 22}
rendered = flask.render_template('index.html', **context)
with app.app_context():
template = '{{ name }} is {{ age }} years old.'
context = {'name': 'bob', 'age': 22}
rendered = flask.render_template_string(template, **context)
Alternatively you could bypass Flask and go directly to Jinja2:
import jinja2
template = jinja2.Template('{{ name }} is {{ age }} years old.')
rendered = template.render(name='Ginger', age=10)
Update
It appears that you might be wanting to stream content back to the requesting client. If so you could write a generator. Something like this might work:
import time
from flask import Flask, Response, render_template_string
from flask import stream_with_context
app = Flask(__name__)
#app.route("/")
def server_1():
def generate_output():
age = 0
template = '<p>{{ name }} is {{ age }} seconds old.</p>'
context = {'name': 'bob'}
while True:
context['age'] = age
yield render_template_string(template, **context)
time.sleep(5)
age += 5
return Response(stream_with_context(generate_output()))
app.run()
Here is some documentation on streaming with Flask.
i have a webservice that takes data from mongodb and returns it over a local web service.
I want to implement a query based control over here that would allow filtering the webservice data from a GUI.
Currently my html file is using the code:
<!DOCTYPE html>
<html lang="en">
<body>
<h1>Welcome to App</h1>
<h2>Enter your username</h2>
<form action="." method="POST">
<input type="text" name="text">
<input type="submit" name="my-form" value="Submit">
</form>
</body>
</html>
and my app.py which is running on flask looks like:
from flask import Flask
from flask import render_template
from flask import request
from pymongo import Connection
import json
from bson import json_util
from bson.json_util import dumps
app = Flask(__name__)
MONGODB_HOST = '172.16.1.95'
MONGODB_PORT = 27017
DBS_NAME = 'AllTables'
COLLECTION_NAME = 'SubjectViews'
#app.route('/')
def my_form():
return render_template("index.html")
#app.route('/testtag', methods=['POST'])
def my_form():
text = request.form['text']
processed_text = " + text + "
return processed_text
def donorschoose_projects():
connection = Connection(MONGODB_HOST, MONGODB_PORT)
collection = connection[DBS_NAME][COLLECTION_NAME]
projects = collection.find({"tags":"processed_text"})
json_projects = []
for project in projects:
json_projects.append(project)
json_projects = json.dumps(json_projects, default=json_util.default)
connection.disconnect()
return json_projects
if __name__ == "__main__":
app.run(host='0.0.0.0',port=5001,debug=True)
What i am trying to do is to get a input from the user in index.html, pass it into the collection.find parameter in app.py so that on my webservice URL i will get data filtered for that particular user. Unfortunately i am running into errors.
Any help would be much appreciated.
Thank you,
Anmol
EDIT: No luck so far!
I have a Flask route that looks like this:
#app.route('/')
def home():
return render_template(
'home.html',
greeting:"hello"
)
How do I test that the 'home.html' template was rendered, and that the render_template() context defined the greeting variable with a particular value?
These should be (and probably are) pretty easy to test, but I'm really not sure how to do this with Flask and unittest.
You can use the assert_template_used method of TestCase provided by flask-testing.
from flask.ext.testing import TestCase
class MyTest(TestCase):
def create_app(self):
return myflaskapp
def test_greeting(self):
self.app.get('/')
self.assert_template_used('hello.html')
self.assert_context("greeting", "hello")
The method create_app must provide your flask app.
Flask official documentation suggests that you use the template_rendered signal (available since version 0.6) for unit-testing your templates and the variables used to render it.
For example, here is a helper context manager that can be used in a unittest to determine which templates were rendered and what variables were passed to the template:
from flask import template_rendered
from contextlib import contextmanager
#contextmanager
def captured_templates(app):
recorded = []
def record(sender, template, context, **extra):
recorded.append((template, context))
template_rendered.connect(record, app)
try:
yield recorded
finally:
template_rendered.disconnect(record, app)
This can now easily be paired with a test client:
with captured_templates(app) as templates:
rv = app.test_client().get('/')
assert rv.status_code == 200
assert len(templates) == 1
template, context = templates[0]
assert template.name == 'index.html'
assert len(context['items']) == 10
My suggestion would be to take a look at the Flask documentation for testing.
Using the docs as a guide you should be able to set up a test case that can check the contents of the response.
import unittest
import yourappname
class MyAppTestCase(unittest.TestCase):
def setUp(self):
self.app = yourappname.app.test_client()
def test_greeting(self):
rv = self.app.get('/')
self.assertIn('hello', rv.data)
where yourappname is the name of your app/project.
You may want to use Jinja setup in your Html page, pass the variable to the page and see if updated.
http://flask.pocoo.org/docs/0.11/templating/#jinja-setup
http://jinja.pocoo.org/
As an example:
flask template
#app.route('/')
def home():
return render_template(
'home.html',
greetingDictionary = {"greeting": "hello" , "forthoseabouttorock" :"wesaluteyou" }
)
html page
{% for key in greetingDictionary %}
<h1>{{key}}</h1>
<p>{{greetingDictionary[key]}}</p>
{% endfor %}