Flask, Routing, and Templates
Build server side apps with Flask, manage routes, and render Jinja templates effectively.
Content
Routing and URL building
Versions:
Watch & Learn
AI-discovered learning video
Sign in to watch the learning video for this topic.
Flask Routing and URL Building — The Cheat Code for Linking Things Together
"Routing is just mapping URLs to code. URL building is the part where you make sure the map doesn’t send users into a lava pit." — your slightly anxious TA
You already know about the Flask application factory pattern (nice!), and we've practiced JSON serialization and date/time handling for reliable data passing. Now we stitch those skills into how requests actually find their handlers — and how your code builds links to those handlers in a safe, DRY way.
What this covers (quick snapshot)
- How Flask matches incoming URLs to view functions (routing basics)
- How to build URLs programmatically with
url_for(reverse routing) - Dynamic segments, converters, blueprints, app-factory context, and common pitfalls
- How this ties to JSON and date/time: serializing things you might put into URLs
Why this matters
- Hardcoding URLs is brittle. If you rename a view or change a prefix (hello, blueprints!), links break.
url_forkeeps your code refactor-safe and compatible with the app factory pattern.- Useful for templates, redirects, AJAX endpoints, API links inside JSON, and generating absolute links in emails.
Routing basics: Map a path to a function
Micro explanation
A route is a pattern. When a request's path matches the pattern, Flask calls the associated function (view) and returns its response.
Example:
@app.route('/')
def index():
return render_template('index.html')
@app.route('/users/<int:user_id>')
def profile(user_id):
return f"Profile for user {user_id}"
/<int:user_id>is a dynamic segment using theintconverter. Other converters:string(default),float,path(allows slashes),uuid,any.
Tip
Use converters to make your URL patterns precise and avoid accidental string parsing later.
URL building with url_for — reverse routing
Micro explanation
Instead of manually writing /users/42, ask Flask to build the URL for you: url_for('profile', user_id=42). Flask returns the proper path based on the endpoint name.
Examples:
from flask import url_for
# inside a view or template context
url_for('profile', user_id=42) # -> '/users/42'
url_for('static', filename='css/site.css') # -> '/static/css/site.css'
# query strings: extra args not declared as route variables become query params
url_for('search', q='flask', page=2) # -> '/search?q=flask&page=2'
# absolute URL
url_for('profile', user_id=42, _external=True) # -> 'http://example.com/users/42'
url_for('profile', user_id=42, _external=True, _scheme='https') # -> 'https://example.com/users/42'
# fragment anchor
url_for('docs', page='routing', _anchor='section-3') # '/docs?page=routing#section-3'
In templates you call the same function with Jinja:
<a href="{{ url_for('profile', user_id=user.id) }}">View profile</a>
Blueprints + app factory = endpoint namespacing
If you use the application factory (create_app) and blueprints (you should), endpoint names are prefixed by the blueprint name.
Example blueprint shop:
# shop/views.py
bp = Blueprint('shop', __name__)
@bp.route('/product/<int:id>')
def product(id):
pass
# When registered with url_prefix='/store', the route becomes '/store/product/<id>'
Call it with:
url_for('shop.product', id=7) # -> '/store/product/7' (url_prefix applied at registration)
This pairs beautifully with the app factory. If your factory registers different blueprints in test vs prod, url_for keeps everything consistent.
Common pitfalls & troubleshooting
BuildError: "Could not build URL for endpoint..."
- Means you passed wrong endpoint name or forgot required route args.
- If you're calling
url_foroutside a request context (e.g., in a script), wrap it inwith app.test_request_context():orwith app.app_context():.
Trailing slashes:
/users/vs/users— Flask treats them differently. If you define/users/, a request to/userswill 301 redirect to/users/. Usestrict_slashes=Falseon the route or be deliberate.
Overlapping routes: order matters when using converters like
pathandstring. Be explicit with converters to avoid surprises.Using untrusted input in URL parts: sanitize or validate before constructing URLs that embed user input.
Using URLs in JSON and with datetimes
You're familiar with JSON serialization and date/time handling. Quick rules when generating links inside JSON responses:
- Use
url_for(..., _external=True)to produce absolute URLs clients can fetch. - If you include timestamps in query params, serialize them (e.g., ISO 8601 via
dt.isoformat()); avoid rawdatetimeobjects which aren't JSON serializable.
Example:
from datetime import datetime
from flask import jsonify, url_for
@app.route('/next-event')
def next_event():
dt = datetime.utcnow()
link = url_for('events.at', ts=dt.isoformat(), _external=True)
return jsonify({ 'next_event_at': dt.isoformat(), 'link': link })
On the client, parse ISO strings back into datetimes (or use a date library).
Small practical checklist (so your links stop betraying you)
- Always use
url_forin templates and Python code to avoid hardcoded paths. - Remember blueprint endpoints:
blueprint_name.view_func_name. - Use converters to validate route parameters early.
- For emails, social previews, or external APIs, use
_external=Trueand_scheme='https'. - When including datetimes in URLs or JSON responses, serialize them to ISO strings.
- If
url_forraises BuildError outside request handling, create an app context.
Key takeaways
- Routing: maps patterns to functions. Use converters to make them robust.
- URL building:
url_foris the DRY way to produce paths and full URLs — use it everywhere. - App factory + blueprints:
url_forkeeps everything consistent across environments.
This is the moment where the concept finally clicks: when you change a route in one place and every link in your app updates automatically. It's boring, but it is also tiny, beautiful engineering magic.
Keep experimenting: rename an endpoint, change a blueprint prefix, then run your tests. If nothing breaks, you win. If something breaks, the traceback will point you to a BuildError and you get to exercise your detective skills. Either way, you learned something.
Happy routing — may your URLs always resolve and your redirects be kind.
Comments (0)
Please sign in to leave a comment.
No comments yet. Be the first to comment!