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

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'.

Related

Can I use Flask Restful alongside an existing Flask App?

I have been tasked with working on an existing Flask project (Flask with Templates/Jinja2 style monolith application). I have to add new features to this app and I'm also intending to re-design the app so it becomes a more micro-services based architecure (i.e. initially Flask-restful based backend with React based front-end). Can I just use Flask-restful by just wrapping the existing app and start creating the new endpoints using Resource?
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
Are there any specific caveats/gotcha's I need to worry about?
Let's try it and see what happens. We start with a basic Flask app:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def index():
return "This is index\n"
#app.route("/endpoint1")
def endpoint1():
return "This is endpoint1\n"
This works and we can request the / and /endpoint1 endpoints and get the expected response:
$ curl localhost:5000
This is index
$ curl localhost:5000/endpoint1
This is endpoint1
Let's see if we can mash a flask_restful managed endpoint in there without disrupting the existing functionality:
from flask import Flask, make_response
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class Widgets(Resource):
def get(self):
return make_response('Yes we have no widgets today\n')
api.add_resource(Widgets, '/widgets')
#app.route("/")
def index():
return "This is index\n"
#app.route("/endpoint1")
def endpoint1():
return "This is endpoint1\n"
Are our original routes still active?
$ curl localhost:5000
This is index
$ curl localhost:5000/endpoint1
This is endpoint1
How about the new one?
$ curl localhost:5000/widgets
Yes we have no widgets today
It looks like the answer is "yes"!

Registering multiple restplus blueprints on same prefix doesn't work

So I'm building an API with Flask-RestPlus and using blueprints to divide the code into smaller chunks, but when trying to register multiple API endpoint, providing same URL prefix, only one blueprint registers.
Blueprint Templates:
from flask import Blueprint
from flask_restplus import Api, Resource
tmpl_bp = Blueprint('templates_api', __name__)
api = Api(tmpl_bp)
ns_tmpl = api.namespace('templates', description='Templates operations')
#ns_tmpl.route('/')
class Templates(Resource):
def get(self):
return "All templates"
def post(self):
return "Added/updated template"
Blueprint Render:
from flask import Blueprint
from flask_restplus import Api, Resource
rend_bp = Blueprint('render_api', __name__)
api = Api(rend_bp)
ns_render = api.namespace('render', description='Render actions')
#ns_render.route('/')
class Render(Resource):
def post(self):
return "Rendering everything"
The main app code, where registering happens:
from flask import Flask, render_template
from api.templates import tmpl_bp
from api.render import rend_bp
app = Flask(__name__)
app.register_blueprint(tmpl_bp, url_prefix="/api/v1")
app.register_blueprint(rend_bp, url_prefix="/api/v1")
#app.route('/')
def home():
return "This is the main page"
The resulting Swagger API:
I was expecting both the Templates and the Render blueprints to be served on /api/v1/ as /api/v1/templates and /api/v1/render respectively. But only one registers every time.
How do I get both blueprints served under the same prefix?
You don't need to use Blueprint, it's enough to use flask_restplus Namespaces.
Flask-RESTPlus provides a way to use almost the same pattern as Flask's blueprint. The main idea is to split your app into reusable namespaces.
from flask import Flask, render_template
from flask_restplus import Resource, Api
from api.templates import tmpl_bp
from api.render import rend_bp
app = Flask(__name__)
api = Api(app)
api.add_namespace(tmpl_bp, path="/api/v1/templates")
api.add_namespace(rend_bp, path="/api/v1/render")
#api.route('/')
def home():
return "This is the main page"
You can find out more details here https://flask-restplus.readthedocs.io/en/stable/scaling.html

Flask route at / returns 404 when used with Flask-Restplus

I have a Flask app which has a Flask-RestPlus API as well as a "/" route. When I try to access "/" however, I get a 404. If I remove the Flask-RestPlus extension, the route works. How do I make both parts work together?
from flask import Flask
from flask_restplus import Api
app = Flask(__name__)
api = Api(app, doc="/doc/") # Removing this makes / work
#app.route("/")
def index():
return "foobar"
This is an open issue in Flask-RestPlus. As described in this comment on that issue, changing the order of the route and Api solves the issue.
from flask import Flask
from flask_restplus import Api
app = Flask(__name__)
#app.route("/")
def index():
return "foobar"
api = Api(app, doc="/doc/")
flask-restplus defines a different way of assigning routes according to their docs:
#api.route('/')
class Home(Resource):
def get(self):
return {'hello': 'world'}
Notice that the api variable is used instead of the app. Moreover, a class is used although I am not 100% sure it is required.

Getting flask-restful routes from within a blueprint

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)]

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.

Categories