- 环境搭建:安装必要的工具。
- 项目结构:如何组织一个 Flask 项目。
- 路由与视图:处理 URL 请求并返回网页。
- 模板:使用 Jinja2 模板引擎动态生成 HTML。
- 静态文件:管理 CSS、JavaScript 和图片。
- 数据存储:使用 SQLite 数据库存储文章。
- 表单处理:创建一个表单来发布新文章。
实例目标:构建一个简单的个人博客
- 首页 ():显示所有博客文章列表。
- 文章详情页 (
/post/<int:id>):显示单篇文章的完整内容。 - 新建文章页 (
/create):一个表单,用于创建和发布新文章。
第一步:环境搭建
在开始之前,请确保您的电脑上已经安装了 Python 3,您可以在终端或命令提示符中输入 python --version 来检查。
-
创建项目目录
mkdir my_blog cd my_blog
-
创建虚拟环境 虚拟环境可以帮助您为每个项目隔离依赖,避免版本冲突。
- 在 macOS/Linux 上:
python3 -m venv venv source venv/bin/activate
- 在 Windows 上:
python -m venv venv .\venv\Scripts\activate
激活后,您的终端提示符前会出现
(venv)。
- 在 macOS/Linux 上:
-
安装 Flask
pip install Flask
第二步:创建项目结构
一个清晰的项目结构对于维护大型项目至关重要,对于我们的博客,结构如下:
my_blog/
├── venv/ # 虚拟环境目录
├── static/ # 存放静态文件 (CSS, JS, images)
│ └── style.css
├── templates/ # 存放 HTML 模板文件
│ ├── base.html
│ ├── index.html
│ └── post.html
├── app.py # Flask 应用主文件
└── database.db # SQLite 数据库文件 (稍后会自动创建)
手动创建这些目录和空的文件。
第三步:编写 Flask 应用主文件 (app.py)
这是我们应用的核心,我们将在这里定义路由、数据库模型和逻辑。
打开 app.py 文件,输入以下代码:
import sqlite3
from flask import Flask, render_template, request, url_for, flash, redirect
# --- 数据库初始化 ---
def get_db_connection():
conn = sqlite3.connect('database.db')
conn.row_factory = sqlite3.Row # 允许通过列名访问数据
return conn
# --- 创建数据库表 ---
def init_db():
conn = sqlite3.connect('database.db')
cursor = conn.cursor()
# 创建 posts 表
cursor.execute('''
CREATE TABLE IF NOT EXISTS posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT NOT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)
''')
# 插入一些示例数据 (如果表为空)
cursor.execute("SELECT COUNT(*) FROM posts")
if cursor.fetchone()[0] == 0:
cursor.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
('第一篇博客', '这是我的第一篇博客内容!'))
cursor.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
('Flask', 'Flask 是一个轻量级的 Python Web 框架。'))
conn.commit()
conn.close()
# --- Flask 应用初始化 ---
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your secret key here' # 用于安全地闪现消息
# --- 路由和视图函数 ---
@app.route('/')
def index():
"""显示所有博客文章"""
conn = get_db_connection()
posts = conn.execute('SELECT * FROM posts ORDER BY created DESC').fetchall()
conn.close()
return render_template('index.html', posts=posts)
@app.route('/post/<int:id>')
def post(id):
"""显示单篇博客文章"""
conn = get_db_connection()
post = conn.execute('SELECT * FROM posts WHERE id = ?', (id,)).fetchone()
conn.close()
if post is None:
flash('文章未找到!')
return redirect(url_for('index'))
return render_template('post.html', post=post)
@app.route('/create', methods=('GET', 'POST'))
def create():
"""创建新博客文章"""
if request.method == 'POST':
title = request.form['title']
content = request.form['content']
if not title:
flash('标题不能为空!')
elif not content:
flash('内容不能为空!')
else:
conn = get_db_connection()
conn.execute('INSERT INTO posts (title, content) VALUES (?, ?)',
(title, content))
conn.commit()
conn.close()
flash('文章已成功发布!')
return redirect(url_for('index'))
return render_template('create.html')
# --- 主程序入口 ---
if __name__ == '__main__':
# 在实际生产环境中,不应该使用 run()
init_db() # 启动时初始化数据库
app.run(debug=True)
代码解释:
get_db_connection(): 连接到 SQLite 数据库database.db。init_db(): 检查数据库表是否存在,如果不存在则创建,并插入一些示例数据,这确保了每次运行应用时都有一个可用的数据库。app = Flask(__name__): 创建 Flask 应用实例。app.config['SECRET_KEY']: 设置一个密钥,用于flash()消息功能。@app.route('/'): 定义 URL 路由,当用户访问网站根目录时,index()函数会被执行。render_template(): 渲染 HTML 模板,并可以传递 Python 变量(如posts=posts)。@app.route('/post/<int:id>'): 动态路由,<int:id>部分会匹配一个整数,并作为参数传递给post()函数。@app.route('/create', methods=('GET', 'POST')): 这个路由可以处理 GET 请求(显示表单)和 POST 请求(提交表单数据)。
第四步:创建 HTML 模板
模板文件位于 templates 目录下,我们使用 Jinja2 语法,它允许我们在 HTML 中嵌入 Python 代码。
基础模板 (templates/base.html)
这是所有页面的“骨架”,其他模板会继承它。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">{% block title %}我的博客{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<nav>
<a href="{{ url_for('index') }}">首页</a>
<a href="{{ url_for('create') }}">新建文章</a>
</nav>
<hr>
<div class="content">
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="flashes">
{% for message in messages %}
<p>{{ message }}</p>
{% endfor %}
</div>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
</body>
</html>
首页模板 (templates/index.html)
继承 base.html,并显示文章列表。
{% extends 'base.html' %}
{% block title %}首页 - 我的博客{% endblock %}
{% block content %}
<h1>我的博客</h1>
{% if posts %}
{% for post in posts %}
<article class="post-preview">
<h2><a href="{{ url_for('post', id=post.id) }}">{{ post['title'] }}</a></h2>
<p class="post-meta">发布于: {{ post['created'] }}</p>
<p>{{ post['content'][:100] }}...</p>
</article>
{% endfor %}
{% else %}
<p>还没有任何文章。</p>
{% endif %}
{% endblock %}
文章详情页 (templates/post.html)
继承 base.html,显示单篇文章。
{% extends 'base.html' %}
{% block title %}{{ post['title'] }} - 我的博客{% endblock %}
{% block content %}
<article class="post-full">
<h1>{{ post['title'] }}</h1>
<p class="post-meta">发布于: {{ post['created'] }}</p>
<div class="post-content">
<p>{{ post['content'] }}</p>
</div>
</article>
{% endblock %}
新建文章页 (templates/create.html)
继承 base.html,包含一个表单。
{% extends 'base.html' %}
{% block title %}新建文章 - 我的博客{% endblock %}
{% block content %}
<h1>新建文章</h1>
<form method="post">
<div class="form-group">
<label for="title">标题</label>
<input type="text" id="title" name="title" required>
</div>
<div class="form-group">
<label for="content">内容</label>
<textarea id="content" name="content" rows="15" required></textarea>
</div>
<div class="form-group">
<button type="submit">发布</button>
</div>
</form>
{% endblock %}
第五步:添加 CSS 样式 (static/style.css)
为了让我们的博客看起来更美观,添加一些简单的 CSS。
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: #f4f4f4;
color: #333;
}
nav {
background: #333;
padding: 1rem 0;
}
nav a {
color: #fff;
text-decoration: none;
margin: 0 15px;
}
hr {
border: 0;
height: 1px;
background: #ccc;
margin: 20px 0;
}
.content {
max-width: 800px;
margin: auto;
background: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1, h2 {
color: #444;
}
.post-preview {
border-bottom: 1px solid #eee;
padding-bottom: 20px;
margin-bottom: 20px;
}
.post-preview h2 a {
text-decoration: none;
color: #007bff;
}
.post-meta {
font-size: 0.8em;
color: #777;
margin-bottom: 10px;
}
.post-full {
margin-top: 20px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="text"],
textarea {
width: 100%;
padding: 8px;
box-sizing: border-box; /* Important for 100% width to work */
border: 1px solid #ddd;
border-radius: 4px;
}
button {
display: inline-block;
background: #007bff;
color: #fff;
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #0056b3;
}
.flashes p {
color: #d9534f;
background: #f2dede;
border: 1px solid #ebccd1;
padding: 10px;
margin-bottom: 15px;
border-radius: 4px;
}
第六步:运行应用
所有文件都已就绪,回到您的终端(确保虚拟环境已激活),在 my_blog 目录下运行:
flask run
您应该会看到类似这样的输出:
* Serving Flask app 'app'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
* Debugger PIN: ...
打开您的网页浏览器,访问 http://127.0.0.1:5000。
您将看到:
- 首页:显示两篇示例文章。
- 点击文章标题,可以进入文章详情页。
- 点击右上角的“新建文章”,可以填写表单并发布新文章,发布成功后,会自动跳转回首页,并显示新发布的文章。
总结与进阶
恭喜!您已经成功使用 Python 和 Flask 构建了一个功能完整的动态网站。
这个实例涵盖的核心技术点:
- Flask 框架:用于处理 Web 请求和响应。
- Jinja2 模板:用于动态生成 HTML 页面。
- SQLite 数据库:用于持久化存储数据。
- 表单处理:接收和验证用户输入。
- 静态文件:管理 CSS 等前端资源。
您可以如何进一步扩展这个项目?
- 用户认证:使用
Flask-Login等扩展添加用户登录和注册功能,让只有登录用户才能创建或编辑文章。 - 富文本编辑器:在前端使用像 TinyMCE 或 CKEditor 这样的富文本编辑器,让文章格式更丰富。
- 分页:当文章很多时,添加分页功能。
- RESTful API:使用
Flask-RESTful或Flask-Smorest创建 API,为移动端或其他前端应用提供数据。 - 部署上线:将您的应用部署到云服务器上,例如使用 Heroku、PythonAnywhere 或 Docker。
