How do I fix an endpoint error with flask blueprints - python

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.

Related

Flask: get current blueprint webroot

I am building a Flask app with a blueprint mounted on two different endpoint (one is a legacy alias to the other).
In my blueprint class:
ldp = Blueprint('ldp', __name__)
#ldp.route('/<path:uuid>', methods=['GET'])
#ldp.route('/', defaults={'uuid': None}, methods=['GET'],
strict_slashes=False)
def get_resource(uuid):
# Route code...
In my main server code:
app = Flask(__name__)
app.config.update(config['flask'])
app.register_blueprint(ldp, url_prefix='/new_ep')
# Legacy endpoint. #TODO Deprecate.
app.register_blueprint(ldp, url_prefix='/old_ep')
How can I get the actual URL of the request up to the /old_ep or /new_ep part in the route method, e.g. http://localhost:5000/new_ep?
So far I have used
request.host_url + request.path.split('/')[1]
but it looks quite inelegant and possibly error-prone. I would like to use the information from the blueprint setup if possible.
Thanks for your help.
EDIT: I could get to the Blueprint instance from within the request with
current_app.blueprints[request.blueprint]
and I was hoping that the url_prefix attribute that I set when registering the blueprint was there, but it is None instead. As I read from the documentation for the supposedly related iter_blueprints() method, apparently these blueprints are listed without regard of how many times and with which parameters they were registered. Too bad.
Here is a full working example to get the idea based off issue 612
from flask import Flask, Blueprint, url_for, request, g
bp = Blueprint('whatever', __name__)
#bp.url_defaults
def bp_url_defaults(endpoint, values):
url_prefix = getattr(g, 'url_prefix', None)
if url_prefix is not None:
values.setdefault('url_prefix', url_prefix)
#bp.url_value_preprocessor
def bp_url_value_preprocessor(endpoint, values):
g.url_prefix = values.pop('url_prefix')
#bp.route('/something')
def index():
return 'host prefix is %s%s' % (request.host_url, g.url_prefix)
app = Flask(__name__)
app.register_blueprint(bp, url_prefix='/new_ep', url_defaults={'url_prefix': 'new_ep'})
app.register_blueprint(bp, url_prefix='/old_ep', url_defaults={'url_prefix': 'old_ep'})

Flask: getting url_prefix for blueprint in view

I have a blueprint with one view. I would like to get url_prefix of blueprint inside view. Unfortunately test.url_prefix returns None. Is there any other way?
app.register_blueprint(test_blueprint, url_prefix = "/test")
#test.route("/task", methods=["GET"])
def task_view(user):
task_url = test.url_prefix + "/task" # test.url_prefix is None ??
Yes.
In Flask the route path of the current view is contained in the url_rule.rule subproperty of the request variable.
So you can do the following:
from flask import request
...
test_blueprint = Blueprint('test', __name__, url_prefix='/test')
...
#test_blueprint.route("/task", methods=["GET"])
def task_view(user):
task_url = request.url_rule.rule
....
app.register_blueprint(test_blueprint)
The value of task_url will be:
/test/task
as desired.
from flask import Blueprint
admin_panel = Blueprint('admin', __name__, template_folder='templates', static_folder='static')
#admin_panel.route('/')
def index():
url_prefix=admin_panel.name
print(url_prefix)
pass

How to use Flasgger with Flask applications using Blueprints?

I am adding Swagger UI to my Python Flask application using Flasgger. Most common examples on the Internet are for the basic Flask style using #app.route:
from flasgger.utils import swag_from
#app.route('/api/<string:username>')
#swag_from('path/to/external_file.yml')
def get(username):
return jsonify({'username': username})
That works.
In my application however, I am not using #app.route decorators to define the endpoints. I am using flask Blueprints. Like following:
from flask import Flask, Blueprint
from flask_restful import Api, Resource
from flasgger.utils import swag_from
...
class TestResourceClass(Resource):
#swag_from('docs_test_get.yml', endpoint='test')
def get() :
print "This is the get method for GET /1.0/myapi/test endpoint"
app = Flask(__name__)
my_api_blueprint = Blueprint('my_api', __name__)
my_api = Api(my_api_blueprint)
app.register_blueprint(my_api_blueprint, url_prefix='/1.0/myapi/')
my_api.add_resource(TestResourceClass, '/test/'
endpoint='test',
methods=['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
....
As seen above, I used #swag_from decorator on the TestResourceClass.get() method which is bound to the GET method endpoint. I also have the endpoint=test matching in the two places.
But I am not getting anything on the Swagger UI, it is all blank. The docs_test_get.yml file does contain the valid yaml markup to define the swagger spec.
What am I missing? How can I get Flasgger Swagger UI working with Flask Blueprint based setup?
Now there is an example of blueprint app in https://github.com/rochacbruno/flasgger/blob/master/examples/example_blueprint.py
"""
A test to ensure routes from Blueprints are swagged as expected.
"""
from flask import Blueprint, Flask, jsonify
from flasgger import Swagger
from flasgger.utils import swag_from
app = Flask(__name__)
example_blueprint = Blueprint("example_blueprint", __name__)
#example_blueprint.route('/usernames/<username>', methods=['GET', 'POST'])
#swag_from('username_specs.yml', methods=['GET'])
#swag_from('username_specs.yml', methods=['POST'])
def usernames(username):
return jsonify({'username': username})
#example_blueprint.route('/usernames2/<username>', methods=['GET', 'POST'])
def usernames2(username):
"""
This is the summary defined in yaml file
First line is the summary
All following lines until the hyphens is added to description
the format of the first lines until 3 hyphens will be not yaml compliant
but everything below the 3 hyphens should be.
---
tags:
- users
parameters:
- in: path
name: username
type: string
required: true
responses:
200:
description: A single user item
schema:
id: rec_username
properties:
username:
type: string
description: The name of the user
default: 'steve-harris'
"""
return jsonify({'username': username})
app.register_blueprint(example_blueprint)
swag = Swagger(app)
if __name__ == "__main__":
app.run(debug=True)
You just need to add your blueprint's name when you reference endpoint. Blueprints create namespaces. Example below. And useful tip: use app.logger.info(url_for('hello1')) for debugging problems with endpoint - it shows very useful error messages like this Could not build url for endpoint 'hello1'. Did you mean 'api_bp.hello1' instead?.
from flask import Flask, Blueprint, url_for
from flask_restful import Api, Resource
from flasgger import Swagger, swag_from
app = Flask(__name__)
api_blueprint = Blueprint('api_bp', __name__)
api = Api(api_blueprint)
class Hello(Resource):
#swag_from('hello1.yml', endpoint='api_bp.hello1')
#swag_from('hello2.yml', endpoint='api_bp.hello2')
def get(self, user=''):
name = user or 'stranger'
resp = {'message': 'Hello %s!' % name}
return resp
api.add_resource(Hello, '/hello', endpoint='hello1')
api.add_resource(Hello, '/hello/<string:user>', endpoint='hello2')
app.register_blueprint(api_blueprint)
swagger = Swagger(app)
app.run(debug=True)
swag_from function have some error to parse file path.you can use doc string to define api in get() first.
flasgger will parse MethodView() method like get,post.
Seems flasgger does not work or has no proper support for blue print style Flask definitions (yet). I used https://github.com/rantav/flask-restful-swagger which worked great!

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>

Flask, Blueprint, current_app

I am trying to add a function in the Jinja environment from a blueprint (a function that I will use into a template).
Main.py
app = Flask(__name__)
app.register_blueprint(heysyni)
MyBluePrint.py
heysyni = Blueprint('heysyni', __name__)
#heysyni.route('/heysyni'):
return render_template('heysyni.html', heysini=res_heysini)
Now in MyBluePrint.py, I would like to add something like :
def role_function():
return 'admin'
app.jinja_env.globals.update(role_function=role_function)
I will then be able to use this function in my template. I cannot figure out how I can access the application since
app = current_app._get_current_object()
returns the error:
working outside of request context
How can I implement such a pattern ?
The message error was actually pretty clear :
working outside of request context
In my blueprint, I was trying to get my application outside the 'request' function :
heysyni = Blueprint('heysyni', __name__)
app = current_app._get_current_object()
print(app)
#heysyni.route('/heysyni/')
def aheysyni():
return 'hello'
I simply had to move the current_app statement into the function. Finally it works that way :
Main.py
from flask import Flask
from Ablueprint import heysyni
app = Flask(__name__)
app.register_blueprint(heysyni)
#app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run(debug=True)
Ablueprint.py
from flask import Blueprint, current_app
heysyni = Blueprint('heysyni', __name__)
#heysyni.route('/heysyni/')
def aheysyni():
# Got my app here
app = current_app._get_current_object()
return 'hello'

Categories

Resources