I would like to be able to generate a navbar based on an object's contents, basically having a navbar array or class that holds subpages and generates the appropriate navbar with collapsing parts etc. I have experience with Laravel in PHP which is similar to Flask but I can't figure out a way to do it easily. I would have to provide a set of data objects to every single page since it's part of the layout but would prefer not to have to specify it specifically for each page. Is there a way to do this?
So far I only have the basics, an app factory, view and blueprint:
Factory
def create_app():
app = Flask(__name__)
app.config.from_pyfile('config.py')
app.register_blueprint(blueprint_index)
return app
Blueprint
from flask import Blueprint, render_template, session
blueprint_index = Blueprint('simple_page', __name__)
#blueprint_index.route('/')
def index():
if 'text' in session:
session['text'] += 1
else:
session['text'] = 0
return render_template('pages/index.html', text=str(session['text']))
Ignore the little bit of debug text I added to the route.
You could do this with a context processor. Add this to the end of your blueprint code-block:
#blueprint_index.context_processor
def inject_text():
return dict(text=session['text'])
{{text}} will now be available within every template on that blueprint.
Related
The flask-rest-jsonapi quickstart shows that you can create a route() like this:
api.route(PostList, 'post_list', '/posts')
api.route(PostDetail, 'post_detail', '/posts/<int:id>')
But I want to have all of my routes to be something like /api/posts and /api/poss/<int:id> and I want to avoid repeating the /api part in every route(). When I try to use a blueprint here, like this:
api_bp = Blueprint('API', __name__, url_prefix='/api')
api = Api(app, api_bp)
api.route(PostList, 'post_list', '/posts')
api.route(PostDetail, 'post_detail', '/posts/<int:id>')
app.register_blueprint(api_bp)
The endpoint is still /posts and not /api/posts. How do I properly make a URL prefix for all the routes?
Reading the discussions on Github do the following:
# Create blueprint
api_bp = Blueprint('API', __name__, url_prefix='/api')
# Create Api instance only passing the blueprint
api = Api(blueprint=api_bp)
# register routes
api.route(PostList, 'post_list', '/posts')
api.route(PostDetail, 'post_detail', '/posts/<int:id>')
# initialize Api instance to App
api.init_app(app)
Don't forget that the view names will change. i.e view 'post_list' becomes 'API.post_list' so you have to adjust your schemes, but not your route declarations. This is also discussed in the linked Github discussion.
You can use flask and Flask-RESTful here. You just need to create flask APP first and configure Flask-RESTful API with prefix.
Here is the example
from flask import Flask, request
from flask_restful import Api
app = Flask(__name__)
api = Api(app, prefix="/api")
Now you can add resource to the api.route as below
import types
api.route = types.MethodType(api_route, api)
def api_route(self, *args, **kwargs):
"""Add resource to the api route"""
def wrapper(cls):
self.add_resource(cls, *args, **kwargs)
return cls
I want to implement simple site layout:
/ must render home.html
/one, /two, /three must render one.html, two.html, three.html correspondingly
So far I came up with following code:
main_page = Blueprint('main', __name__)
category_page = Blueprint('category', __name__)
#main_page.route("/")
def home():
return render_template('home.html')
#category_page.route('/<category>')
def show(category):
return render_template('{}.html'.format(category))
app = Flask(__name__)
app.register_blueprint(main_page, url_prefix='/')
app.register_blueprint(category_page, url_prefix='/categories')
This way I am able to route categories to /categories/<category>. How can I route them to just /<category> instead, while keeping home.html linked to /? Appreciate your help
I tried two approaches:
Setting url_prefix='/' for both blueprints => second one does not work.
Instead of main_page blueprint use just app.route('/') to render home.html. This one also does not work when mixing with category_page blueprint
You can move the variable to the url_prefix parameter inside the registering statement :
#main_page.route("/")
def home():
return render_template('home.html')
app.register_blueprint(main_page, url_prefix='/')
#category_page.route('/')
def show(category):
return render_template('{}.html'.format(category))
app.register_blueprint(category_page, url_prefix='/<category>')
(it depends on the complexity of the whole pattern, but it may be better to keep the registering statements with the variables close to each function to handle many views.)
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'})
I have two application factory functions - one creates the "customer" app, and the other creates the "admin" backend app. Both of the factory functions essentially do what is described here - create a flask app and register some extensions to it and then add some blueprints(with a url_prefix). I glue the two apps together via the create_combined_app() from below. It is the return value of that function which I register with my Flask-Script's Manager.
def create_combined_app(config_name):
customer_app = create_customer_app(config_name)
admin_app = create_admin_app(config_name)
from werkzeug.wsgi import DispatcherMiddleware
customer_app.wsgi_app = DispatcherMiddleware(customer_app.wsgi_app, {
'/admin': admin_app
})
return customer_app
And then this is how I run it.
def make_me_an_app():
return create_combined_app(config)
manager = Manager(make_me_an_app)
...
if __name__ == '__main__':
manager.run()
I want to do some testing which involves getting all GET routes of my app and making sure they load. I followed the example from here, but I only see the urls of the customer app, and none of the urls from the admin backend.
#main.route("/site-map")
def site_map():
from flask import current_app, jsonify
links = []
app = current_app
for rule in app.url_map.iter_rules():
if "GET" in rule.methods and has_no_empty_params(rule):
url = url_for(rule.endpoint, **(rule.defaults or {}))
links.append((url, rule.endpoint))
return jsonify(links)
The admin backend works when I try to access it from the browser - it all works nicely, except that I don't see the admin's urls when I call /site-map.
Thanks! :)
I think DispatcherMiddleware create separate apps. Which mean you created customer_app and admin_app. Those 2 live as standalone. They don't know each others, therefor current_app is just the show customer_app.
Here is the describe from Flask http://flask.pocoo.org/docs/0.12/patterns/appdispatch/
I'm new to Flask and I'm about to write a larger application. So far I'v divided functionality into blueprints. Now I want to be able to set some global variable from within a blueprint during initialization of it (so outside of a request context). Basically, I want to automatically initialize a navigation list for certain blueprints, so each blueprint needs to tell my base application how it wants to be named and what its default route is.
My goal is that other people can extend my application by just putting their custom blueprint into my application's "plugin" folder. In this scenario, my application doesn't know their routes or names. It needs to automatically learn it while loading the specific blueprint...
To explain it in some other way: I have a main application containing some sub applications implemented as blueprints. The main application should hold a navigation bar referencing to all sub applications (blueprints). How can the blueprint register something in that main menu navigation variable on (e.g. on initialization)?
(I didn't find a way to access something like "self.parent" or the application context from a blueprint. Don't blueprints have something like a constructor?)
so each blueprint needs to tell my base application how it want's to
be named and what it's default route is.
When you create a blueprint you already pass its name in the first parameter:
simple_page = Blueprint('simple_page')
You can pass to the constructor the url_prefix value too
simple_page = Blueprint('simple_page', url_prefix='/pages')
I have a main application containing some sub applications implemented
as blueprints. The main application should hold a navigation bar
referencing to all sub applications (blueprints)
This is an example in one python module, you should split each blueprint in its own module.
from flask import Flask, Blueprint, render_template
# ADMIN
admin = Blueprint('admin', __name__, url_prefix='/admin')
#admin.route('/')
def admin_index():
return 'Admin module'
#admin.route('/settings')
def settings():
return 'Admin Settings'
# USER
user = Blueprint('user', __name__, url_prefix='/user')
#user.route('/')
def user_index():
return 'User module'
#user.route('/profile')
def profile():
return 'User Profile'
app = Flask(__name__)
app.register_blueprint(admin)
app.register_blueprint(user)
#app.route('/')
def index():
return render_template('index.html', blueprints=app.blueprints)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True, port=7000)
The Jinja2 template. Remember to place this file in the templates folder in you root project, which is where flask search the templates by default.
<ul>
{% for bp_name, bp in blueprints.iteritems() %}
<li>{{ bp.name }}</li>
{% endfor %}
</ul>
From the flask main application object, in this case app, you can access the list of blueprints registered; app.blueprints, which is a python dictionary with the name of the blueprint that you passed in the constructor. So you pass this object to you template and loops on it to render the name and URL in the menu.
Furthermore, what if I want to enable blueprint authors to pass more
data in a standardized way (e.g. in which color to display the link to
his piece, or whatever...)
As this is a blueprint specific data, I think a good solution is to extend the Blueprint class and implement a custom method to save extra data in a standardized way and then access then from the main application object.
custom.py
from flask import Blueprint
class MyBlueprint(Blueprint):
bp_data = {}
def set_data(data):
# here you can make extra task like ensuring if
# a minum group of value were provided for instance
bp_data = data
admin.py
from .custom import MyBlueprint
admin = MyBlueprint('admin', __name__, url_prefix='/admin')
admin.set_data({'color': '#a569bd', 'enabled': true})
# all the blueprint's routes
...
app.py
from admin import admin
app = Flask(__name__)
app.register_blueprint(admin)
#app.route('/')
def index():
for a, b in app.blueprints:
print b.bp_data['color']
print b.bp_data['enabled']
...
Of course, my custom Blueprint class needs more work on it, like validating what type of data its being passed or throwing an error if there isn't a required value, like; title, require_auth, etc. From this point, it's you who must define what is the minimum required data that a blueprint must provide to your main application to work properly.
Here I present you a extensible app pattern that I usually use. It is proved that work fine.
Directory structure:
YourApp
|- plugins (will contain all the user's plugins)
|- __init__.py (empty)
|-plugin1
|- __init__.py (empty)
|- loader.py
|- app.py
app.py
from flask import Flask
import os
def plugin_loader(app):
""" Function for discover new potencial plugins.
After checking the validity (it means, check if it implements
'register_as_plugin', load the user defined blueprint"""
plugins = [x for x in os.listdir('./plugins')
if os.path.isdir('./plugins/' + x)]
for plugin in plugins:
# TODO: Add checking for validation
module = __import__('plugins.' + str(plugin) + '.loader', fromlist=['register_as_plugin'])
app = module.register_as_plugin(app)
return app
# Creates the flask app
app = Flask(__name__)
# Load the plugins as blueprints
app = plugin_loader(app)
print(app.url_map)
#app.route('/')
def root():
return "Web root!"
app.run()
plugins/plugin1/loader.py
from flask import Blueprint
plugin1 = Blueprint('plugin1', __name__)
#plugin1.route('/version')
def version():
return "Plugin 1"
def register_as_plugin(app):
app.register_blueprint(plugin1, url_prefix='/plugin1')
return app
So futures plugins should implement the register_as_plugin function, inside the loader.py file, inside their directory in plugins/.
For more information about directories discovery you can read https://docs.python.org/2/library/os.path.html.
For more information about the dynamic import of modules you can read https://docs.python.org/2/library/functions.html#import.
Proved with Python 2.7.6, Flask 0.10.1 and Unix platform.