View functions — render article list and detail pages
In this chapter, you will learn to query data from the database using view functions, render the homepage and detail pages, and split routes using Blueprint.
The responsibilities of view functions
Flask view functions have the same responsibilities as Django:Receive request → Query data → Render template。
Example
from flask import Flask, render_template
from models import Post, Category
@app.route("/")
def index():
"""Blog home page: displays all articles"""
posts = Post.query.order_by(Post.created_at.desc()).all()
categories = Category.query.all()
return render_template('index.html', posts=posts, categories=categories)
Path parameters and query parameters
| Parameter Type | URL Example | How views are obtained |
|---|---|---|
| Path Parameter | /post/3 | In routes, <type:variable_name> → function parameter |
| Query Parameter | /?category=python | request.args.get('key') |
Flask routes support multiple type converters:<int:id>(integer),<string:slug>(default, does not include /),<path:path>(contains /),<float:price>etc.
Article Detail Page
Example
from flask import abort
@app.route("/post/<int:post_id>")
def post_detail(post_id):
"""Article detail page"""
# get_or_404 is equivalent to: Post.query.get(post_id) or abort(404)
post = Post.query.get_or_404(post_id,
description=f'Article #{post_id} does not exist')
return render_template('post_detail.html', post=post)
Custom 404 error page
Example
def not_found(error):
return render_template('404.html'), 404
Category Filtering
Example
from flask import request
@app.route("/")
def index():
category_slug = request.args.get('category', '')
keyword = request.args.get('q', '') # Search keyword (implemented in Chapter 6)
posts_query = Post.query.order_by(Post.created_at.desc())
# Filter by category
if category_slug:
posts_query = posts_query.join(Category).filter(Category.slug == category_slug)
posts = posts_query.all()
categories = Category.query.all()
return render_template('index.html',
posts=posts, categories=categories,
category_slug=category_slug, keyword=keyword)
request.argsis an immutable MultiDict, used to read URL query parameters. Use.get('key', '默认值')Safe retrieval, returns a default value when the parameter does not exist instead of raising an error.
Blueprint — splitting routes
When view functions increase, stacking them all in app.py becomes unmanageable.
BlueprintIt is Flask's route grouping scheme: split views by functional modules and register via app.register_blueprint().
app/ ├── blueprints/ │ ├── __init__.py │ ├── main.py # 首页、关于、搜索 │ └── posts.py # 文章列表、详情 ├── templates/ ├── models.py └── __init__.py
Example
from flask import Blueprint, render_template, request
from app.models import Post, Category
# Blueprint('blueprint_name', __name__, url_prefix='/')
main_bp = Blueprint('main', __name__)
@main_bp.route("/")
def index():
"""Blog home page"""
category_slug = request.args.get('category', '')
posts_query = Post.query.order_by(Post.created_at.desc())
if category_slug:
posts_query = posts_query.join(Category).filter(Category.slug == category_slug)
return render_template('index.html',
posts=posts_query.all(),
categories=Category.query.all(),
category_slug=category_slug)
Example
from flask import Blueprint, render_template, abort
from app.models import Post
posts_bp = Blueprint('posts', __name__)
@posts_bp.route("/post/<int:post_id>")
def post_detail(post_id):
post = Post.query.get_or_404(post_id)
return render_template('post_detail.html', post=post)
Register the Blueprint at the application entry point:
Example
from flask import Flask
from app.blueprints.main import main_bp
from app.blueprints.posts import posts_bp
app = Flask(__name__)
# Register blueprints: all view functions of the blueprints will be merged into the same application
app.register_blueprint(main_bp)
app.register_blueprint(posts_bp)
A Blueprint can specify
url_prefix(such asurl_prefix='/blog'), then all routes within the blueprint will automatically have this prefix added. A Blueprint can also have its own template and static file directories.
Creating templates for the homepage and detail page
index.html
Example
{% extends 'base.html' %}
{% block title %}EXAMPLE Blog - Home{% endblock %}
{% block content %}
<h2 class="section-title">Latest Articles</h2>
<!-- Category filter navigation -->
<div class="category-bar">
<a href="{{ url_for('main.index') }}"
class="{% if not category_slug %}active{% endif %}">All</a>
{% for cat in categories %}
<a href="{{ url_for('main.index', category=cat.slug) }}"
class="{% if category_slug == cat.slug %}active{% endif %}">
{{ cat.name }}
</a>
{% endfor %}
</div>
{% if posts %}
<div class="article-grid">
{% for post in posts %}
<a href="{{ url_for('posts.post_detail', post_id=post.id) }}" class="card-link">
<div class="article-card">
<div class="card-content">
<span class="card-category">{{ post.category.name }}</span>
<h3>{{ post.title }}</h3>
<p>{{ post.summary|truncate(80) }}</p>
<span class="card-date">{{ post.created_at.strftime('%Y-%m-%d') }}</span>
</div>
</div>
</a>
{% endfor %}
</div>
{% else %}
<p class="empty-tip">No articles yet in this category.</p>
{% endif %}
{% endblock %}
When using a Blueprint view in url_for(), the format is
url_for('蓝图名.函数名'), such asurl_for('posts.post_detail', post_id=3). Note the difference from Django's{% url %}tag comparison: Flask uses{{ url_for() }}, can be nested in Python expressions for more flexibility.
Chapter summary
In this chapter, you mastered Flask views and route organization: Post.query to query data, path parameter <int:post_id> and query parameter request.args, get_or_404 for safe queries, abort(404) and custom error pages, and Blueprint to split routes by module.
The blog now has three complete pages: the homepage article list, category filtering, and detail pages.
other extensions