在Flask中,通过 render_template() 函数将数据从视图传递到模板。数据以关键字参数的形式传递,在模板中作为变量使用。
最基本的传递方式:在 render_template() 中传递关键字参数。
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
# 传递简单变量
username = "张三"
age = 25
score = 95.5
is_vip = True
return render_template(
'index.html',
username=username,
age=age,
score=score,
is_vip=is_vip
)
@app.route('/user/<user_id>')
def user_profile(user_id):
# 直接传递参数
return render_template(
'profile.html',
user_id=user_id,
title=f"用户 {user_id} 的资料"
)
<!DOCTYPE html>
<html>
<head>
<title>用户信息</title>
</head>
<body>
<h1>用户信息</h1>
<div class="user-info">
<p>用户名: {{ username }}</p>
<p>年龄: {{ age }}</p>
<p>分数: {{ score }}</p>
{% if is_vip %}
<span class="badge vip">VIP会员</span>
{% endif %}
{% if score > 90 %}
<p class="success">优秀成绩!</p>
{% endif %}
</div>
<!-- 使用三元表达式 -->
<p>状态: {{ "活跃" if is_vip else "普通" }}</p>
</body>
</html>
可以直接传递表达式的结果,而不是先计算再传递。
# 视图函数
@app.route('/products')
def products():
products = [
{'name': '商品A', 'price': 100},
{'name': '商品B', 'price': 200},
{'name': '商品C', 'price': 150}
]
# 计算并传递多个值
return render_template(
'products.html',
products=products,
total_products=len(products), # 直接计算长度
total_price=sum(p['price'] for p in products), # 计算总价
avg_price=sum(p['price'] for p in products) / len(products) if products else 0
)
<!-- 模板中使用 -->
<h2>商品列表 (共 {{ total_products }} 件)</h2>
<p>总价值: ¥{{ total_price }}</p>
<p>平均价格: ¥{{ "%.2f"|format(avg_price) }}</p>
<ul>
{% for product in products %}
<li>
{{ product.name }} - ¥{{ product.price }}
{% if product.price > avg_price %}
<span class="text-warning">(高于平均)</span>
{% endif %}
</li>
{% endfor %}
</ul>
可以传递任何Python数据结构,包括列表、字典、元组等。
@app.route('/dashboard')
def dashboard():
# 复杂数据结构示例
user_data = {
'profile': {
'name': '李四',
'email': 'lisi@example.com',
'joined': '2023-01-15'
},
'preferences': {
'theme': 'dark',
'notifications': True,
'language': 'zh-CN'
},
'stats': {
'posts': 42,
'followers': 156,
'following': 89
}
}
# 嵌套列表
recent_activities = [
{'type': 'post', 'title': '发布了新文章', 'time': '2小时前'},
{'type': 'comment', 'title': '评论了帖子', 'time': '5小时前'},
{'type': 'like', 'title': '点赞了照片', 'time': '1天前'}
]
# 传递所有数据
return render_template(
'dashboard.html',
user=user_data,
activities=recent_activities,
is_weekend=True
)
<!-- 访问嵌套字典 -->
<h2>{{ user.profile.name }} 的仪表板</h2>
<p>邮箱: {{ user.profile.email }}</p>
<!-- 访问嵌套字典的值 -->
{% if user.preferences.theme == 'dark' %}
<body class="dark-theme">
{% endif %}
<!-- 遍历列表中的字典 -->
<h3>最近活动</h3>
<ul class="activity-list">
{% for activity in activities %}
<li class="{{ activity.type }}">
<span>{{ activity.title }}</span>
<small>{{ activity.time }}</small>
</li>
{% endfor %}
</ul>
<!-- 使用字典的items()方法 -->
<div class="stats">
{% for key, value in user.stats.items() %}
<div class="stat-item">
<strong>{{ key }}:</strong>
<span>{{ value }}</span>
</div>
{% endfor %}
</div>
可以直接传递Python对象,在模板中访问其属性和方法。
# models.py
class User:
def __init__(self, id, name, email):
self.id = id
self.name = name
self.email = email
self.created_at = datetime.now()
def get_display_name(self):
return f"{self.name} ({self.email})"
def is_recent_user(self):
# 检查是否在7天内注册
delta = datetime.now() - self.created_at
return delta.days < 7
# app.py
from models import User
@app.route('/user/<user_id>')
def user_detail(user_id):
# 模拟从数据库获取用户
user = User(
id=user_id,
name="王五",
email="wangwu@example.com"
)
# 传递对象
return render_template('user_detail.html', user=user)
<!-- 模板中访问对象 -->
<div class="user-card">
<h2>{{ user.get_display_name() }}</h2>
<p>用户ID: {{ user.id }}</p>
<p>注册时间: {{ user.created_at.strftime('%Y-%m-%d') }}</p>
{% if user.is_recent_user() %}
<span class="badge bg-success">新用户</span>
{% endif %}
<!-- 调用带参数的方法 -->
{% set greeting = user.say_hello("朋友") %}
<p>{{ greeting }}</p>
</div>
上下文处理器允许你在所有模板中自动注入变量,无需在每个视图函数中重复传递。
from datetime import datetime
import os
@app.context_processor
def inject_common_variables():
"""注入所有模板共用的变量"""
return {
# 当前时间
'current_time': datetime.now(),
# 应用配置
'app_name': '我的Flask应用',
'app_version': '1.0.0',
# 环境变量
'debug_mode': app.debug,
# 工具函数
'get_year': lambda: datetime.now().year,
# 常用常量
'COMPANY_NAME': 'Acme Inc.',
'SUPPORT_EMAIL': 'support@example.com'
}
@app.context_processor
def inject_user_data():
"""注入用户相关数据"""
# 假设有函数获取当前用户
user = get_current_user() # 自定义函数
return {
'current_user': user,
'is_logged_in': user is not None,
'is_admin': user.is_admin if user else False
}
<!-- 在任何模板中都可以直接使用 -->
<!DOCTYPE html>
<html>
<head>
<title>{{ app_name }} - {{ page_title }}</title>
<meta name="version" content="{{ app_version }}">
</head>
<body>
<header>
<h1>{{ app_name }}</h1>
{% if is_logged_in %}
<p>欢迎,{{ current_user.name }}!</p>
{% endif %}
</header>
<main>
<p>当前时间: {{ current_time.strftime('%Y-%m-%d %H:%M:%S') }}</p>
<p>© {{ get_year() }} {{ COMPANY_NAME }}</p>
{% if debug_mode %}
<div class="debug-info">
调试模式已开启
</div>
{% endif %}
</main>
<footer>
<p>联系我们: {{ SUPPORT_EMAIL }}</p>
</footer>
</body>
</html>
可以根据请求或条件动态注入变量。
@app.context_processor
def inject_request_based_data():
"""基于请求的上下文处理器"""
from flask import request
# 获取请求信息
path = request.path
method = request.method
# 根据路径决定显示的内容
show_sidebar = not path.startswith('/admin/')
show_analytics = 'dashboard' in path
return {
'current_path': path,
'request_method': method,
'show_sidebar': show_sidebar,
'show_analytics': show_analytics,
# 生成面包屑导航
'breadcrumbs': generate_breadcrumbs(path)
}
def generate_breadcrumbs(path):
"""生成面包屑导航数据"""
parts = path.strip('/').split('/')
breadcrumbs = []
for i, part in enumerate(parts):
url = '/' + '/'.join(parts[:i+1])
name = part.replace('-', ' ').title()
breadcrumbs.append({'name': name, 'url': url})
return breadcrumbs
Flask和Jinja2提供了一些内置的全局变量,无需传递即可使用。
| 变量 | 描述 | 示例 |
|---|---|---|
request |
当前请求对象 | {{ request.url }} |
session |
会话对象 | {{ session.get('user_id') }} |
g |
请求全局对象 | {{ g.user }} |
config |
应用配置 | {{ config['DEBUG'] }} |
url_for() |
URL生成函数 | {{ url_for('index') }} |
get_flashed_messages() |
获取闪现消息 | {{ get_flashed_messages() }} |
<!-- 使用request对象 -->
<div class="request-info">
<p>当前URL: {{ request.url }}</p>
<p>请求方法: {{ request.method }}</p>
<p>用户代理: {{ request.user_agent.browser }}</p>
{% if request.args.get('page') %}
<p>页码: {{ request.args.get('page') }}</p>
{% endif %}
</div>
<!-- 使用session -->
{% if session.logged_in %}
<p>欢迎用户 {{ session.username }}</p>
<a href="{{ url_for('logout') }}">退出登录</a>
{% else %}
<a href="{{ url_for('login') }}">请登录</a>
{% endif %}
<!-- 使用config -->
{% if config.DEBUG %}
<div class="debug-notice">
调试模式已开启
</div>
{% endif %}
<!-- 使用url_for生成URL -->
<nav>
<a href="{{ url_for('home') }}">首页</a>
<a href="{{ url_for('profile', username='john') }}">John的资料</a>
<a href="{{ url_for('static', filename='css/style.css') }}">CSS文件</a>
</nav>
<!-- 显示闪现消息 -->
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="flash-messages">
{% for message in messages %}
<div class="alert alert-info">{{ message }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
可以传递Python函数到模板中直接调用。
# 定义一些工具函数
def format_price(price):
"""格式化价格显示"""
return f"¥{price:,.2f}"
def truncate_text(text, length=100):
"""截断文本并添加省略号"""
if len(text) <= length:
return text
return text[:length] + "..."
def get_gravatar_url(email, size=80):
"""根据邮箱获取Gravatar头像URL"""
import hashlib
email_hash = hashlib.md5(email.lower().encode()).hexdigest()
return f"https://www.gravatar.com/avatar/{email_hash}?s={size}&d=identicon"
def is_featured(product):
"""判断商品是否推荐"""
return product.get('featured', False) or product['sales'] > 100
@app.route('/shop')
def shop():
products = [
{'name': '商品A', 'price': 1234.56, 'description': '这是一个很长的商品描述...', 'sales': 150},
{'name': '商品B', 'price': 89.99, 'description': '短描述', 'sales': 80}
]
# 传递函数到模板
return render_template(
'shop.html',
products=products,
format_price=format_price,
truncate_text=truncate_text,
get_gravatar_url=get_gravatar_url,
is_featured=is_featured
)
<div class="products">
{% for product in products %}
<div class="product-card {{ 'featured' if is_featured(product) else '' }}">
<h3>{{ product.name }}</h3>
<!-- 调用传递的函数 -->
<p class="price">{{ format_price(product.price) }}</p>
<p class="description">
{{ truncate_text(product.description, 50) }}
</p>
{% if is_featured(product) %}
<span class="badge bg-warning">推荐商品</span>
{% endif %}
<!-- 调用带参数的函数 -->
<img src="{{ get_gravatar_url('user@example.com', 40) }}"
alt="头像">
</div>
{% endfor %}
</div>
<!-- 直接调用函数 -->
<p>格式化测试: {{ format_price(1234567.89) }}</p>
# 技巧1: 使用字典展开简化代码
user_data = {
'username': 'john',
'email': 'john@example.com',
'age': 30
}
config = {
'site_name': '我的网站',
'debug': True
}
# 传统方式
return render_template(
'index.html',
username=user_data['username'],
email=user_data['email'],
age=user_data['age'],
site_name=config['site_name'],
debug=config['debug']
)
# 简化方式(Python 3.5+)
return render_template(
'index.html',
**user_data, # 展开字典
**config # 展开另一个字典
)
# 技巧2: 条件传递
def user_profile():
user = get_current_user()
extra_data = {}
if user and user.is_admin:
extra_data = {
'admin_stats': get_admin_stats(),
'recent_logs': get_recent_logs()
}
return render_template(
'profile.html',
user=user,
**extra_data # 只有管理员才传递这些数据
)
<!-- 技巧3: 模板中的默认值 -->
<p>用户名: {{ username|default('匿名用户') }}</p>
<p>邮箱: {{ email|default('未设置', true) }}</p>
<!-- 技巧4: 使用set创建局部变量 -->
{% set full_name = user.first_name ~ ' ' ~ user.last_name %}
<h2>{{ full_name }}</h2>
<!-- 技巧5: 处理可能不存在的变量 -->
{% if user.metadata is defined and user.metadata.theme %}
<body class="{{ user.metadata.theme }}-theme">
{% else %}
<body class="default-theme">
{% endif %}
<!-- 技巧6: 使用with简化嵌套访问 -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
# blog_views.py
from flask import render_template, request
from models import Post, User, Comment
from utils import paginate_query
@app.route('/blog')
def blog_index():
"""博客首页"""
page = request.args.get('page', 1, type=int)
# 获取分页数据
posts_query = Post.query.filter_by(published=True).order_by(Post.created_at.desc())
pagination = paginate_query(posts_query, page, per_page=10)
# 获取热门文章
popular_posts = Post.get_popular_posts(limit=5)
# 获取标签云
tags = Post.get_tag_cloud()
# 获取统计数据
stats = {
'total_posts': Post.query.count(),
'total_comments': Comment.query.count(),
'total_authors': User.query.count()
}
return render_template(
'blog/index.html',
posts=pagination.items, # 当前页的文章
pagination=pagination, # 分页对象
popular_posts=popular_posts,
tags=tags,
stats=stats,
current_year=datetime.now().year,
page_title='博客首页',
meta_description='欢迎访问我们的技术博客,分享编程技巧和开发经验。'
)
@app.route('/blog/<slug>')
def blog_post(slug):
"""博客文章详情页"""
post = Post.query.filter_by(slug=slug).first_or_404()
# 增加阅读量
post.increment_views()
# 获取相关文章
related_posts = post.get_related_posts(limit=3)
# 获取文章评论
comments = post.get_comments(approved_only=True)
# 获取作者信息
author = post.author
return render_template(
'blog/post.html',
post=post,
related_posts=related_posts,
comments=comments,
author=author,
page_title=post.title,
meta_description=post.excerpt,
meta_keywords=','.join([tag.name for tag in post.tags])
)
<!-- blog/index.html -->
{% extends "base.html" %}
{% block title %}{{ page_title }}{% endblock %}
{% block content %}
<div class="row">
<!-- 左侧:文章列表 -->
<div class="col-md-8">
<h1>技术博客</h1>
<p class="lead">共有 {{ stats.total_posts }} 篇文章</p>
{% for post in posts %}
<article class="blog-post">
<h2>
<a href="{{ url_for('blog_post', slug=post.slug) }}">
{{ post.title }}
</a>
</h2>
<div class="post-meta">
<span>
<i class="fas fa-user"></i>
{{ post.author.username }}
</span>
<span>
<i class="fas fa-calendar"></i>
{{ post.created_at|date('Y-m-d') }}
</span>
<span>
<i class="fas fa-eye"></i>
{{ post.views }} 阅读
</span>
</div>
<p>{{ post.excerpt|truncate(200) }}</p>
<div class="post-tags">
{% for tag in post.tags %}
<a href="{{ url_for('tag_posts', tag=tag.slug) }}"
class="badge bg-secondary">
{{ tag.name }}
</a>
{% endfor %}
</div>
</article>
{% endfor %}
<!-- 分页导航 -->
{% if pagination.pages > 1 %}
<nav aria-label="Page navigation">
<ul class="pagination">
{% if pagination.has_prev %}
<li class="page-item">
<a class="page-link"
href="?page={{ pagination.prev_num }}">
上一页
</a>
</li>
{% endif %}
{% for page_num in pagination.iter_pages() %}
<li class="page-item {{ 'active' if page_num == pagination.page else '' }}">
<a class="page-link" href="?page={{ page_num }}">
{{ page_num }}
</a>
</li>
{% endfor %}
{% if pagination.has_next %}
<li class="page-item">
<a class="page-link"
href="?page={{ pagination.next_num }}">
下一页
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
<!-- 右侧:侧边栏 -->
<div class="col-md-4">
<!-- 热门文章 -->
<div class="sidebar-widget">
<h3>热门文章</h3>
<ul>
{% for post in popular_posts %}
<li>
<a href="{{ url_for('blog_post', slug=post.slug) }}">
{{ post.title }}
</a>
</li>
{% endfor %}
</ul>
</div>
<!-- 标签云 -->
<div class="sidebar-widget">
<h3>标签云</h3>
<div class="tag-cloud">
{% for tag in tags %}
<a href="{{ url_for('tag_posts', tag=tag.slug) }}"
style="font-size: {{ tag.font_size }}px;">
{{ tag.name }}
</a>
{% endfor %}
</div>
</div>
</div>
</div>
{% endblock %}
通过本教程,你已经掌握了Flask中传递数据到模板的各种方法:
render_template() 传递简单变量