flask inner blueprint cannot find templates - python

I'm creating a website for my CV using flask for front-end and back-end.
Each page contains a layout of an header, footer & profile info + container of the current selected content.
In order to support this structure I've created a package called contact that contains several blueprints (blog_blueprint, homepage_blueprint, etc...) and register them to the application on import
my project structure is:
Clerification - there are no templates with the same name under different blueprints
first of all, Is that considered a good practice? if not - what are the recommandations for case like this?
second, while running the code and enter it I'm getting an error indecating that it cannot find somepage.html in my homepage_blueprint...
homepage blueprint code:
from project import db
from project.models import About, Service, Skill
from flask import render_template
from flask.blueprints import Blueprint
homepage_blueprint = Blueprint('Home Page', __name__)
#homepage_blueprint.route('/home/')
def homepage():
return render_template(
'homepage.html',
about=_get_about_me(),
services=_get_my_services(),
desing_skills=_get_design_skills(),
code_skills=_get_code_skills())
####################
# Helper functions #
####################
def _get_about_me():
return About.query.one()
def _get_my_services():
return Service.query.all()
def _get_design_skills():
return _get_skill('Design')
def _get_code_skills():
return _get_skill('Code')
def _get_skill(type: str):
return db.session.query(Skill).filter_by(type=type).all()

Related

Create Flask views that render different templates without repeating code

I want to define multiple endpoints that render different templates, without writing out each one. The endpoints are all similar, one looks like:
#app.route('/dashboard/')
def dashboard():
return render_template('endpoints/dashboard.html')
I tried defining a function in a for loop for each endpoint name, but the problem is that the name of the function stays the same and Flask raises an error about that.
routes = ['dashboard', 'messages', 'profile', 'misc']
for route in routes:
#app.route('/' + route + '/')
def route():
return render_template('endpoints/' + route + '.html')
How can I create these views without repeating myself?
You don't want to do this. Instead, use a variable in the route to capture the template name, try to render the template, and return a 404 error if the template doesn't exist.
from flask import render_template, abort
from jinja2 import TemplateNotFound
#app.route('/<page>/')
def render_page(page):
try:
return render_template('endpoints/{}.html'.format(page))
except TemplateNotFound:
abort(404)
Alternatively, and less preferably, you can use the same function name as long as you provide Flask with unique endpoint names. The default name is the name of the function, which is why Flask complains.
for name in routes:
#app.route('/', endpoint=name)
def page():
return render_template('endpoints/{}.html'.format(name))

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

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

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