Test Flask render_template() context - python

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 %}

Related

How to have separate routers module in Flask?

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.

How do I mimic Java Springs #PathVariable using Python Flask

from flask import Flask, jsonify, request
from flask_restful import Api, Resource
app = Flask(__name__)
api = Api(app)
user_dict = {}
class User(Resource):
def __init__(self):
user_id = 0
def get(self):
return jsonify(user_dict[id])
api.add_resource(User, "/user")
if __name__ == "__main__":
app.run(debug=True)
The idea is that when a GET request is made to /user/1, then the get method returns that key/value pair of the user_dict. How do I do path variables in Python? Please assume that the dictionary is not empty.
Flask uses <variable_name> or <converter:variable_name> placeholders in URL path registrations.
This is used in the examples shown in the Flask-Restful Quickstart documentation:
class TodoSimple(Resource):
def get(self, todo_id):
return {todo_id: todos[todo_id]}
def put(self, todo_id):
todos[todo_id] = request.form['data']
return {todo_id: todos[todo_id]}
api.add_resource(TodoSimple, '/<string:todo_id>')
Here <string:todo_id> is a path variable, passed to the TodoSimple.get() and TodoSimple.put() methods as an argument.
Flask-Restful otherwise assumes a general familiarity with Flask's patterns, I strongly recommend you read through at least the Flask Quickstart document, and I recommend you also work through the tutorial, if nothing else.
For your specific example, if user ids are always integers, use:
class User(Resource):
def get(self, user_id):
return jsonify(user_dict[user_id])
api.add_resource(User, "/user/<int:user_id>")

Is there a way to test if flask template contains a link?

I've been testing whether routes exist using
def test_index(self):
r = self.app.get("/")
self.assertEqual(200, r.status_code, "Status code was not 'OK'.")
My template has a hyperlink to another page. Is there a way to test if this exists?
Well, if you are testing templates, every template you render is the result of a request to some route. If you render url's in a template using url_for(), then it will raise a BuildError if the url is pointing to a non existing route, and the server will return the status code 500. Therefore, you don't need to parse your templates manually for testing purposes if you just check the route instead.
Example:
from flask import Flask, render_template_string
app = Flask(__name__)
#app.route('/index')
def index():
return render_template_string("""
{{ url_for('index') }}
{{ url_for('blabla') }}
""")
def test_index(self):
r = self.app.get("/index")
self.assertEqual(200, r.status_code, "Status code was not 'OK'.")
This will result in a
routing.BuildError: Could not build url for endpoint 'blabla'. Did you mean 'static' instead? error, which makes your tests fail.
I hope this explanation is clear enough!

How to apply decorator to all blueprint urls in flask

I have a blueprint and some url functions,
admin_bp = Blueprint('admin', __name__)
#admin_bp.route('/dashboard', methods=['GET', ])
#flask_login.login_required
def dashboard():
context = {}
page = 'admin/dashboard.html'
return render_template(page, **context)
#admin_bp.route('/deny', methods=['GET', ])
#flask_login.login_required
def deny():
return 'hey bro you dont belong here'
I don't want to copy paste #flask_login.login_required decorator for all url functions under this blueprint. Is there a better way that I can apply decorator for all blueprint urls?
You can add before_request() as a function that will run before each request in a view.
You will then want to add decorators to inject additional functionality to the before_request function. You will want to import the login_required decorator to ensure each endpoint requires a logged in user. This decorator is part of the flask_login library.
Since it looks like your views are part of an admin, I'd also recommend adding a custom decorator to your before_request function with something like #role_required('admin'). The functionality for that decorator will live somewhere else and be imported.
#admin_bp.before_request
#login_required
def before_request():
""" Protect all of the admin endpoints. """
pass
Subclass Blueprint and override the route method.
import flask
class MyBlueprint(flask.Blueprint):
def route(self, rule, **options):
def decorator(f):
# these lines are copied from flask.Blueprint.route
endpoint = options.pop("endpoint", f.__name__)
self.add_url_rule(rule, endpoint, f, **options)
# At this point flask.Blueprint.route simply returns f.
# But you can nest a decorator.
def inner(*args, **kwargs):
# stuff you want to do before each request goes here
try:
result = f(*args, **kwargs)
# stuff you want to do on successful responses (probing status, headers, etc.) goes here
except Exception as e:
# stuff you want to do on error responses goes here
raise
return inner
Now use the new subclass in your blueprints:
-v1_blueprint = Blueprint('v1', __name__)
+v1_blueprint = MyBlueprint('v1', __name__)
No changes needed to individual routes.
The drawback of this approach is that it copies code from inside Flask. If the implementation of flask.Blueprint.route were to change in a future version, you'd need to sync MyBlueprint with it when you upgrade Flask.
How about checking the user first:
from flask.ext.login import current_user
#admin_bp.before_request
def check_user():
if not current_user.is_authenticated():
abort(401)
# your other functions without `#flask_login.login_required`

How to pass arbitrary arguments to a flask blueprint?

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>

Categories

Resources