URL prefix in Flask Python deployed to Heroku using Blueprint - python

I'm trying to create a URL prefix for my web app's api. I want the api to return when I enter api.website.com/parameter.I am using Flask and Blueprint
api_bp = Blueprint('api', __name__,
template_folder='templates',
url_prefix='/api')
#api_bp.route("/")
def get_monkey_api():
address = request.args.get('address',
None)
if address and is_a_banano_address(address):
return monkey_api(banano_address)
else:
return render_template("NotABananoAddress.html"), 400
#api_bp.route("/<address>")
def monkey_api(address):
monKey = monkey_data_generator.generate_monKey(address)
if monKey.status is not 'Citizen':
return "API data on vanity monKeys does not exist"
return jsonify(monKey.serialize())
app = Flask(__name__)
app.register_blueprint(api_bp, url_prefix='/api')
Most of the code in unrelated. The fact is when I I am entering
api.website.com?address=xxx
or when I am entering
api.website.com/xxx
I should get my API as JSON back but I'm not. On localhost it doesn't return anything and doesn't show the prints that I even insert into the code and of course on Heroku it does not recognize the project when I using the prefix.

You gave your blueprint a URL prefix:
api_bp = Blueprint('api', __name__,
template_folder='templates',
url_prefix='/api')
and again with
app.register_blueprint(api_bp, url_prefix='/api')
That means you need to use hostname/api/ to get to get_monkey_api() view function, or hostname/api/xxxx to get to the monkey_api() view function.
Remove the URL prefixes if you want the routes to be found at the site root. If you want the blueprint to work for a separate subdomain, then use the subdomain='api' option, not a URL prefix.
Note that for subdomains to work, you need to configure the SERVER_NAME config option so that subdomains can be detected. If you want to test this locally, edit your /etc/hosts file to add some development aliases that point to your server, then set SERVER_NAME to match.

Related

Flask - Can't override default subdomain for blueprints needing a different subdomain

I have a Flask project/app and want it to be served mainly from app.example.com. I also have a single blueprint inside this app which should only be served from api.example.com.
Now, if I set app as the default subdomain, I'm unable to override this default in other blueprints which should be served from a different subdomain (e.g. api). In fact, any blueprints created with a different subdomain will 404.
In other words, the code below doesn't work (api.example.com/test2 will 404):
# -*- coding: utf-8 -*-
from flask import Flask, Blueprint
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com'
app.url_map.default_subdomain = 'app' # set default subdomain, intending to override it below for `api.*`
appbp = Blueprint('app', 'app')
apibp = Blueprint('api', 'api')
#appbp.route('/test1')
def app_hello():
# this works (app.example.com/test1)
return 'appbp.app_hello'
#apibp.route('/test2')
def api_hello():
# this will 404 (api.example.com/test2)
return 'apibp.api_hello'
app.register_blueprint(appbp) # this works, serves from `app.example.com`
app.register_blueprint(apibp, subdomain='api') # doesn't work, `api.example.com/test2` will 404, so will `app.example.com/test2` (tried just in case it was using the default subdomain instead)
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8888, debug=True)
However, if I don't set a default subdomain and instead set a subdomain each time I register a blueprint, it magically works for both app and api:
# -*- coding: utf-8 -*-
from flask import Flask, Blueprint
app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com'
# app.url_map.default_subdomain = 'app' # now try without a default
appbp = Blueprint('app', 'app')
apibp = Blueprint('api', 'api')
#appbp.route('/test1')
def app_hello():
# this works (app.example.com/test1)
return 'appbp.app_hello'
#apibp.route('/test2')
def api_hello():
# this works (api.example.com/test2)
return 'apibp.api_hello'
app.register_blueprint(appbp, subdomain='app') # works, explicitly set subdomain on each blueprint
app.register_blueprint(apibp, subdomain='api') # works, explicitly set subdomain on each blueprint
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8888, debug=True)
In both examples, it appears the blueprints are registered with the correct subdomain:
<Rule 'app|/test1' (OPTIONS, GET, HEAD) -> app.app_hello>
<Rule 'api|/test2' (OPTIONS, GET, HEAD) -> api.api_hello>
But, clearly, there's a difference between setting app.url_map.default_subdomain intending to override it later and just explicitly setting subdomains manually.
Any idea what's going on here?
Bonus points: which of these is the preferred way to set a subdomain? I've seen it done both ways.
app.register_blueprint(apibp, subdomain='api')
vs.
apibp = Blueprint('api', 'api', subdomain='api')
app.register_blueprint(apibp)
What's missing is the subdomain_matching option for Flask():
subdomain_matching – consider the subdomain relative to SERVER_NAME when matching routes. Defaults to False.
Since app is a relative name, you need to set this to True:
app = Flask(__name__, subdomain_matching=True)
This used to be done implicitly, but as of Flask 1.0 it's an explicit switch. The change was made because different people had different expectations of what setting SERVER_NAME means, see Flask issue #998.
As for where you set the subdomain option, that's your choice. If you are reusing blueprints in different contexts, then it makes more sense to set the subdomain option in the app.register_blueprint() call, while setting it in the Blueprint() instance call may make it more self-documenting if you are creating that blueprint object closer to your routes for and so want to make it clearer to someone working on that code that they are affecting a specific subdomain only.

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 Security giving routing error on security.register

While trying to link to Flask-Securities register view in my template with:
<li>Register</li>
I get a routing error
werkzeug.routing.BuildError
werkzeug.routing.BuildError: Could not build url for endpoint 'security.register'. Did you mean 'security.login' instead?
From what I've searched around for, setting the Flask Security config line "SECURITY_REGISTERABLE" to True should have fixed it, and yet it's set to True and I'm still getting this error...
I setup Flask Security in my models.py like so:
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
I just had the exact same error after refactoring my app. In my new app.py I missed these config settings:
# More Flask Security settings
app.config['SECURITY_REGISTERABLE'] = True
app.config['SECURITY_REGISTER_URL'] = '/admin/create_account'
app.config['SECURITY_LOGIN_URL'] = '/admin/login'
app.config['SECURITY_POST_LOGIN_VIEW'] = '/admin'
app.config['SECURITY_LOGOUT_URL'] = '/admin/logout'
app.config['SECURITY_POST_LOGOUT_VIEW'] = '/admin'
app.config['SECURITY_RESET_URL'] = '/admin/reset'
app.config['SECURITY_CHANGE_URL'] = '/admin/change'
app.config['SECURITY_USER_IDENTITY_ATTRIBUTES'] = ['email', 'username']
The example above uses url endpoint inside my "Flask Admin" /admin
In your case i'm not exactly sure: by default flask security should be able to find the template folder and routes in the Flask Security package I think. The quickstart docs don't mention any routes that you've you need to manually setup.
You'll also need a app config with something like this:
app.secret_key = 'SECRETSTUFF'
Probably an good idea to add explicit routes for the register page.
The above error is occuring because there is no register() route available in the security blueprint or may be you have named register route as login() in your code. Plz recheck your code. By my little knowledge in flask-Security i think register view is enabled by default. To define the register view you just need to decorate the route using #security.route('/register). I hope this is useful.

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

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