1. 环境搭建:安装必要的工具。
  2. 项目结构:如何组织一个 Flask 项目。
  3. 路由与视图:处理 URL 请求并返回网页。
  4. 模板:使用 Jinja2 模板引擎动态生成 HTML。
  5. 静态文件:管理 CSS、JavaScript 和图片。
  6. 数据存储:使用 SQLite 数据库存储文章。
  7. 表单处理:创建一个表单来发布新文章。

实例目标:构建一个简单的个人博客

  • 首页 ():显示所有博客文章列表。
  • 文章详情页 (/post/<int:id>):显示单篇文章的完整内容。
  • 新建文章页 (/create):一个表单,用于创建和发布新文章。

第一步:环境搭建

在开始之前,请确保您的电脑上已经安装了 Python 3,您可以在终端或命令提示符中输入 python --version 来检查。

  1. 创建项目目录

    mkdir my_blog
    cd my_blog
  2. 创建虚拟环境 虚拟环境可以帮助您为每个项目隔离依赖,避免版本冲突。

    • 在 macOS/Linux 上:
      python3 -m venv venv
      source venv/bin/activate
    • 在 Windows 上:
      python -m venv venv
      .\venv\Scripts\activate

      激活后,您的终端提示符前会出现 (venv)

  3. 安装 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

您将看到:

  1. 首页:显示两篇示例文章。
  2. 点击文章标题,可以进入文章详情页。
  3. 点击右上角的“新建文章”,可以填写表单并发布新文章,发布成功后,会自动跳转回首页,并显示新发布的文章。

总结与进阶

恭喜!您已经成功使用 Python 和 Flask 构建了一个功能完整的动态网站。

这个实例涵盖的核心技术点:

  • Flask 框架:用于处理 Web 请求和响应。
  • Jinja2 模板:用于动态生成 HTML 页面。
  • SQLite 数据库:用于持久化存储数据。
  • 表单处理:接收和验证用户输入。
  • 静态文件:管理 CSS 等前端资源。

您可以如何进一步扩展这个项目?

  1. 用户认证:使用 Flask-Login 等扩展添加用户登录和注册功能,让只有登录用户才能创建或编辑文章。
  2. 富文本编辑器:在前端使用像 TinyMCE 或 CKEditor 这样的富文本编辑器,让文章格式更丰富。
  3. 分页:当文章很多时,添加分页功能。
  4. RESTful API:使用 Flask-RESTfulFlask-Smorest 创建 API,为移动端或其他前端应用提供数据。
  5. 部署上线:将您的应用部署到云服务器上,例如使用 Heroku、PythonAnywhere 或 Docker。