Getting flask-restful routes from within a blueprint - python

Is there a way to get the routes defined in a blueprint? I know this (http://flask.pocoo.org/snippets/117/) snippit exists, but requires to have the app initalized to use url_map. Is there a way to view the routes without an application? I'm developing an api with flask-restful and I would like to display the routes from within the blueprint without the app to keep it self contained.

Using the information provided by polyfunc, I was able to come up with this solution:
from flask import Blueprint, current_app, url_for
from flask_restful import Resource, Api
api_blueprint = Blueprint('api', __name__)
api = Api(api_blueprint)
#api.resource('/foo')
class Foo(Resource):
def get(self):
prefix = api_blueprint.name + '.' # registered classes will have this as their prefix
# Get current rules
rules = current_app.url_map.iter_rules()
# Get list from routes registered under the blueprint
routes = [url_for(rule.endpoint) for rule in rules if rule.endpoint.startswith(prefix)]

Related

What is the benefit of using Blueprint in Flask-Restful?

I'm new to Flask and the Blueprint concept. I understand the benefit of using Blueprint in a normal Flask application. However, after reading many posts/blogs/documentation, the benefit of using Blueprint with Flask-Restful is still unclear to me.
Let's consider two examples from the Flask-Restful documentation. The first one:
from flask import Flask
from flask_restful import Api
from myapi.resources.foo import Foo
from myapi.resources.bar import Bar
from myapi.resources.baz import Baz
app = Flask(__name__)
api = Api(app)
api.add_resource(Foo, '/Foo', '/Foo/<string:id>')
api.add_resource(Bar, '/Bar', '/Bar/<string:id>')
api.add_resource(Baz, '/Baz', '/Baz/<string:id>')
We have 3 resources. We register them and start the project. Everything is clear at this point. I would be happy with this approach.
Then, they use blueprint:
from flask import Flask, Blueprint
from flask_restful import Api, Resource, url_for
app = Flask(__name__)
api_bp = Blueprint('api', __name__)
api = Api(api_bp)
class TodoItem(Resource):
def get(self, id):
return {'task': 'Say "Hello, World!"'}
api.add_resource(TodoItem, '/todos/<int:id>')
app.register_blueprint(api_bp)
As far as I can see, the code is getting more complicated:
More steps required: create the API instance with the blueprint, register the blueprint with the app,...
If I have another resource, e.g. user, I have to repeat all of those steps (please correct me if I'm wrong):
user_bp = Blueprint('user', __name__)
user_api = Api(user_bp)
user_api.add_resource(User, '/user/<int:id>')
app.register_blueprint(user_bp)
So, what is the benefit of using Blueprint here?
A blueprint in Flask is an object to structure a Flask application into subsets. This helps in organizing code and separating functionality.
For Flask-RESTful this can be used to create an application subset for your api. So for example you have a core blueprint, an auth blueprint and besides that an api blueprint. It can also be useful when you create a new version of your api that breaks backwards compatibility with the first version. In this case you can create a second blueprint for the new version.
You don’t have to create a new blueprint for each api endpoint that you create, you can reuse the api blueprint for each endpoint like:
from flask import Flask, Blueprint
from flask_restful import Api, Resource, url_for
app = Flask(__name__)
core_bp = Blueprint('core', __name__)
api_bp = Blueprint('api', __name__)
api = Api(api_bp)
#core_bp.route('/')
def index():
return 'Index'
class TodoItem(Resource):
def get(self, id):
return {'task': 'Say "Hello, World!"'}
api.add_resource(TodoItem, '/todos/<int:id>')
api.add_resource(User, '/user/<int:id>')
app.register_blueprint(core_bp)
app.register_blueprint(api_bp, url_prefix='/api')
Like this your core application is accessible via '/' and the api via '/api'.

How to use url_map.iter_rules with blueprint object instead of app

I have a blueprint object "api" and a apis.py file where I have many APIs defined with api.route annotation. eg:
#api.route('/', methods=['GET'])
def get_info():
I want to iterate and get summary of all the APIs I have same as what we get when we use "url_map.iter_rules" on app object. How can we do this using api blueprint object? I have registered the blueprint in my init.py file using
from .api_1 import api as api_blueprint
app.register_blueprint(api_blueprint)
I think if you call app.url_map.iter_rules() after you've registered the blueprint you'll get all of the endpoints of the subdomains too, e.g.
api.py
from flask import Blueprint
api = Blueprint('api', __name__)
#api.route('/')
def call_api():
return ""
init.py:
from flask import Flask, Blueprint
from api import api
public = Blueprint('public', __name__)
#public.route('/')
def home():
return render_template('public/home.html')
app = Flask(__name__)
app.register_blueprint(public)
app.register_blueprint(api, subdomain='api')
print(list(app.url_map.iter_rules()))
[<Rule 'api|/' (GET, HEAD, OPTIONS) -> api.call_api>,
<Rule '/' (GET, HEAD, OPTIONS) -> public.home>,
<Rule '/static/<filename>' (GET, HEAD, OPTIONS) -> static>]
In case you find it useful, I made a function that shows me (only for testing) each of the url's according to the blueprints that are registered in the main application.
This is the only solution I have found to be able to print the endpoints separating them by the blueprint they belong to.
Of course you can create a function to be able to print the url_map of only one of the blueprints by passing the name in string format, or the blueprint itself.
Here are the examples:
from flask.logging import create_logger
def log_routes(app: Flask):
log = create_logger(app)
with app.app_context():
"""
Maps every single endpoint for blueprint registered in the main application.
Also shows methos available for each endpoint
"""
log.info('MAP ROUTER')
bps = app.blueprints
for bp in bps:
print('', end='\n')
log.info(f'BLUEPRINT RULES: "{bp}"')
for ep in app.url_map.iter_rules():
bp_name = ep.endpoint.split('.')[0]
if bp_name == bp:
log.debug(f'Endpoint: {ep} methods={ep.methods}')
Here is the example with a function that takes only the name of the blueprint from which you need to get its url_map:
def log_blueprint_urls(app: Flask, bp_name: str):
log = create_logger(app)
with app.app_context():
"""
Maps every single endpoint for an specific blueprint in the main application.
Also shows methos available for each endpoint
"""
bps = app.blueprints
if bp_name in bps:
log.info(f'MAP ROUTER FOR BLUEPRINT "{bp_name}"')
for ep in app.url_map.iter_rules():
bp_name = ep.endpoint.split('.')[0]
if bp_name == bp_name:
log.debug(f'Endpoint: {ep} methods={ep.methods}')
else:
log.critical(
f'BLUEPRINT "{bp_name}" has not registered in main application')

May I build more than two layers of endpoint in Flask?

I know when flask builds large application, it has registered multiple blueprints.
The flask blueprint starts to initial a blueprint object, it has declared name of first layer of endpoint in the same time.
For example:
users_bp = Blueprint('users', __name__)
According to the expression, the name of first layer of endpoint of users_bp is users.
The blueprint object continues to register its view function, it has declared name of second layer of endpoint in the same time.
#users_bp.route('/login')
def login():
# do something
According to the expression, the name of second layer of endpoint of users_bp is login, it's from view name.
If I want to use endpoint to get corresponding url, I should be to do : url_for('users.login').
So it's workflow of building large application from flask tutorial. Is it right?
Let's get back on point. Is it possible to build three layers of endpoint as url_for('api. users.login')?
How do I package the blueprint or flask app to complete my wanted structure? Is it available?
You can set an endpoint within your route decorator, for example:
from flask import Flask, render_template_string
app = Flask(__name__)
#app.route('/', endpoint="this.is.the.home.endpoint")
def index():
_html="<a href='{{url_for('this.is.another.endpoint')}}'>Go to another endpoint</a>"
return render_template_string(_html)
#app.route('/another', endpoint="this.is.another.endpoint")
def another():
_html="<a href='{{url_for('this.is.the.home.endpoint')}}'>Go to the home page</a>"
return render_template_string(_html)
if __name__ == '__main__':
app.run()

Flask-Restful ignores blueprint's url_prefix

I want to use Flask blueprints to organize my Flask-Restful resources into separate url prefixes. Regardless of what url prefix I set (during either blueprint creation or registration), everything gets mapped to the raw route paths. How do I correctly use Restful with blueprints?
app = Flask(__name__)
api = Api(app)
api.add_resource(Building, '/<int:id>', endpoint='building')
api.add_resource(Jack, '/<int:id>', endpoint='jack')
building_api = Blueprint('building_api', __name__)
jack_api = Blueprint('jack_api', __name__)
app.register_blueprint(building_api, url_prefix='/buildings')
app.register_blueprint(jack_api, url_prefix='/jacks')
All documentation I can find says that these should now be available at /buildings/<int:id> and /jacks/<int:id>, but both of those urls 404 and instead I can access the building one at /<int:id>. Hard coding the path in add_resource fixes it, but defeats the point of url_prefix.
You need to pass the blueprint to the Api instance, not the app.
building_bp = Blueprint('buildings', __name__)
building_api = Api(building_bp)
building_api.add_resource(Building, '/<int:id>')
app.register_blueprint(building_bp, url_prefix='/buildings')
This is zhe best way to do with blueprint:
from flask import Flask, Blueprint
from flask_restful import Api, Resource, url_for
app = Flask(__name__)
api_bp = Blueprint('api', __name__)
api = Api(api_bp)
class TodoItem(Resource):
def get(self, id):
return {'task': 'Say "Hello, World!"'}
api.add_resource(TodoItem, '/todos/<int:id>')
app.register_blueprint(api_bp)
you should send Blueprint'instance to Api
I do not know why but I struggled using the Blueprint as mentioned in the answers.
But here's a quick solution I found while going through the doc link. Making use of the prefix parameter in Api() does the job.
app = Flask(__name__)
api = Api(app, prefix='/buildings')
Now, all your routes will be prefixed with /buildings. Just make sure you use url_for('link') in places where you might have simply used a /link.
One more strange thing I noticed is that atleast for me, it did not work until I renamed my routes to the same name as their class names. For example, Class Home(Resource) should have a route to /home. Using /homeepage or any other route for Home Class causes an error. Not sure if it is only me.

URL building with Flask and non-unique handler names

Flask provides a url_for function to generate URLs to handlers based on the URL pattern. But this would imply that the handler functions must have unique names across the entire application. Is that correct?
Example
Module A has a handler index:
#app.route('/')
def index(): pass
And Module B has another handler index:
#app.route('/anotherindex')
def index(): pass
How to distinguish the handlers called index when building URLs?
url_for('index')
I don't know how you could do with all the views routed by the same module.
What I usually do is separate my views in different modules (like you did with module A and B), and register them as blueprints, after that, when using the url_for() function, you can prefix the view name with your blueprint name and then avoid conflicts and potential problems.
Here is an example:
main_views.py:
from flask import Blueprint
main = Blueprint('main', __name__)
#main.route('/')
def index():
pass
admin_views.py:
from flask import Blueprint
admin = Blueprint('admin', __name__)
#admin.route('/admin')
def index():
pass
application.py:
from flask import Flask
from main_views import main
from admin_views import admin
app = Flask('my_application')
app.register_blueprint(main)
app.register_blueprint(admin)
Now, to access the 2 index views and still distinguish one from the other, just use url_for('main.index') or url_for('admin.index')
EDIT:
Just one more useful details about routing using blueprints, when registering the blueprint, you can pass a url_prefix argument, that will apply to every view within this blueprint.
For example, given the following code:
admin_views.py
from flask import Blueprint
admin = Blueprint('admin', __name__)
#admin.route('/')
def index():
pass
#admin.route('/logout')
def logout():
pass
application.py:
from flask import Flask
from admin_views import admin
app = Flask('my_application')
app.register_blueprint(admin, url_prefix='/admin')
The 2 views would be available at the URL /admin/ and /admin/logout

Categories