搭建个人 blog 的初次尝试

Web 开发 发布于 2025-08-10 最后更新于 16 天前


在刚接触到 web 时,我就产生了亲手搭建一个网站的想法。在之后的学习中慢慢了解到了个人博客的概念,也就决定了要搭建一个属于自己的博客。历经一年,终于有了一定的知识储备,当时的想法有机会付诸实践了,我便抽出了几天的时间想要尝试一下。

下面具体记录一下我在这次博客搭建中学到的东西。

静态博客与动态博客

静态博客

  • 所有的文章页面在发布前就已经生成成了固定的 HTML 文件。
  • 浏览者访问时,服务器只是直接把这些 HTML 文件发给浏览器,没有再去运行程序或查询数据库。
  • 常用工具:Hexo、Hugo、Jekyll 等静态网站生成器。

动态博客

  • 页面不是事先生成的,而是访问时临时拼出来的。
  • 服务器会先运行代码(如 Python、PHP、Node.js 等),从数据库读取文章内容,再把 HTML 返回给浏览器。

我理想中的个人博客是动态博客,相对于静态博客来说在发布文章、管理网站和功能实现上都更占优势。

自研动态博客系统

本质上就是自己写一个小型的 CMS(内容管理系统)。

建站方式:Flask + Jinja2 + SQLite

数据库选用 SQLite 的原因

  • SQLite 不需要像 MySQL 或 PostgreSQL 那样进行复杂的安装和配置。数据库的创建仅仅依赖于一个文件。只需要提供数据库文件路径,SQLite 就能立即工作。
  • SQLite 不需要专门的数据库管理员。只要能够访问数据库文件,任何地方都可以操作数据库,非常适合小型项目或个人开发者使用。
  • 对于小型应用,SQLite 提供了非常好的性能。在数据量较小或并发较低时,它的数据读写速度很快。

网站结构

采用 Flask 工厂模式结构建站

Python
flask_blog/
├── app/                         # 主应用包
│   ├── __init__.py             # 创建 Flask 应用实例,注册蓝图,初始化扩展
│   ├── models.py               # 定义数据库模型
│   ├── forms.py                # 定义表单类
│   ├── views/                  # 视图逻辑分区
│   │   ├── __init__.py        # 初始化视图蓝图
│   │   ├── main.py            # 首页蓝图
│   │   ├── ...
│   ├── templates/                 # Jinja2 模板文件夹
│   │   ├── index.html            # 首页模板
│   │   └── ...
│   │   └── admin/
│   │       ├── admin_board.html     # 后台首页模板
│   │       ├── ...
│   ├── static/                # 静态文件目录
│   │   ├── css/
│   │   ├── js/
│   │   └── images/
│   └── extensions.py         # 初始化所有 Flask 扩展

├── migrations/                # Flask-Migrate 数据库迁移目录(执行 flask db init 后生成)

├── config.py                  # 配置文件
├── app.db                     # 数据库文件
├── run.py                     # 项目入口文件,运行 Flask 应用
├── requirements.txt           # 所需 Python 库清单(可选)
└── README.md                  # 项目说明文档(可选)
Python

主要搭建逻辑

  1. 项目初始化和环境准备
    • 创建并激活虚拟环境,安装 Flask 及基础依赖
    • 创建项目文件夹结构
  2. 初始化 Flask 应用工厂
    • app/__init__.py 中创建 create_app() 函数
    • 编写 config.py,定义基础配置(如数据库 URI,密钥等)
    • 编写 app/extensions.py,创建并初始化扩展对象
    • app/__init__.py 中加载配置,初始化 Flask 扩展
  3. 运行入口
    • 编写 run.py,导入 create_app() 创建应用实例
  4. 数据库模型设计
    • 编写 app/models.py,设计文章、用户、评论等模型
    • 定义模型关系
    • 初始化迁移:flask db init创建迁移脚本:flask db migrate应用迁移:flask db upgrade
  5. 视图蓝图框架搭建
    • app/templates/index.html 编写首页模板
    • app/views/main.py 先写首页路由,返回简单的模板测试渲染
    • app/views/__init__.py 导入视图模块,方便注册
    • app/__init__.py 注册蓝图
    这个时候就已经可以运行服务器进行测试了
  6. 其他
    • app/forms.py 设计表单类,如文章发布表单、评论表单、登录表单等
    • app/static/ 目录中放 CSS、JS、图片等静态资源文件

数据库调用

借助 Flask-SQLAlchemy 库实现与 SQLite 数据库的交互。

extensions.py 中调用 Flask-SQLAlchemy 库中的 SQLAlchemy 类,并初始化SQLAlchemy 实例。

models.py 导入 SQLAlchemy 实例。

__init__.py 中绑定扩展实例。

接下来在写视图蓝图逻辑时直接从 models.py 中导入数据库模型类,就可以直接在视图函数中用把数据库的数据赋值给变量了。在 html 模板文件当中就可以用 jinja2 模板语法通过调用变量实现调用数据库。

数据库迁移

如果项目中涉及到数据库结构的修改(如新增表、修改字段等),可以使用 Flask-Migrate 来管理数据库迁移。Flask-Migrate 会自动生成迁移脚本,并应用到数据库中,确保模型变更能够同步到数据库。

管理员用户的实现

通过 session 与 Flask-Login 实现基础功能。

通过 flask 引入 session 全局对象。

在 Flask 中,session 是一个存储在客户端的 字典-like 对象。它允许在用户与应用交互期间存储数据,从而支持在多个请求之间保持用户的状态。每当浏览器发送请求时,Flask 会从请求的 cookies 中读取 session 数据。Flask 会在应用中解析并使用它。

Flask-Login 是一个扩展库,用于在 Flask 应用中处理用户认证和会话管理。它为 Flask 应用提供了用户登录、登出、会话管理等功能。

数据库密码储存

数据库中的密码不以明文储存,经过哈希处理后再存储。哈希是单向加密,不能从哈希值还原出原始的密码。

通过 Werkzeug 库引入 generate_password_hash,check_password_hash 函数,在models.py 中通过模型类方法调用,并在视图函数中完善相关逻辑。

一些具体实现

比如想实现这样一个功能:

博客的首页需要展示文章,每页展示五篇文章,能自动计算文章总数与页数并完成布局。

SQLAlchemy 提供了的 paginate 方法来实现分页功能,这个方法返回一个分页对象,包含以下属性:

  • items:当前页的数据列表。
  • page:当前页码。
  • has_prev:是否有上一页。
  • has_next:是否有下一页。
  • prev_num:上一页页码。
  • next_num:下一页页码。
  • total:总数据量。
  • pages:总页数。

通过 jinja2 语法并调用 SQLite数据库可以这样实现:

html

HTML
<!--文章板块视图-->
{% for post in posts %}
<div class="post">
    <!--文章标题视图-->
    <a href="{{ url_for('text.post_detail', post_id= post.id ) }}">
        <h2 class="post-title">{{ post.title }}</h2>
    </a>
    
    <!--分类,日期视图-->
    <div class="category-and-date">
        <!--文章分类视图-->
        <span class="category">
            <a href="/categories/tutorial/">
                <span class="icon">
                    <i class="fa-solid fa-bookmark fa-fw"></i>
                </span>
                {{ post.category }} 
            </a>
        </span>
        <!--文章日期视图-->
        <span class="date">
            <span class="icon">
                <i class="fa-solid fa-calendar fa-fw"></i>
            </span>
            {{ post.create_at }} 
        </span>
    </div>
    <!--分类,日期视图结束-->

    <!--文章简述视图-->
    <div class="description">
        <div class="content" v-pre>
            <p>
                {{ post.description }}
            </p>
        </div>
    </div>
    <!--文章简述视图结束-->

    <!--标签视图-->
    <div class="post-tags">
        <span class="icon">
            <i class="fa-solid fa-tags fa-fw"></i>
        </span>
        {% for tag in post.tags %}
        <span class="tag">
            <a href="/tags/hexo/" style="color: {{ tag.color }}">{{ tag.name }}</a>
        </span>
        {% endfor %}
    </div>
    <!--标签视图结束-->

    <a href="{{ url_for('text.post_detail', post_id= post.id ) }}" class="go-post">阅读全文</a>
</div>
{% endfor %}
<!--文章板块视图结束-->

<!--分页导航栏视图-->
<div class="page-current">
    <!--页码列表-->
    {% for page_num in pagination.iter_pages(left_edge=1, right_edge=1, left_current=2, right_current=2) %}
        {% if page_num %}
        {% if page_num == pagination.page %}
            <span class="current">{{ page_num }}</span>
        {% else %}
            <a class="page-num" href="{{ url_for('main.index', page=page_num) }}">{{ page_num }}</a>
        {% endif %}
        {% else %}
        <span class="page-omit">...</span>
        {% endif %} 
    {% endfor %}
    <!--下一页按钮-->
    {% if pagination.has_next %}
        <a class="page-num" href="">
        <i class="fa-solid fa-caret-right fa-fw"></i>
        </a>
    {% endif %}
</div>
<!--分页导航栏视图结束-->
HTML

blueprint.py

Python
@main_bp.route('/')
@main_bp.route('/page/<int:page>')
def index(page=1):
    #引入 pagination, 定义分页
    per_page = 5
    pagination = Post.query.order_by(Post.id.desc()).paginate(page=page, per_page=per_page, error_out=False)
    #引入 post 和 tag
    posts = pagination.items
    tags = Tag.query.all()
    return render_template('index.html', posts=posts, tags=tags, pagination=pagination)
Python

Argon

自研动态系统建站本来已经有了一定的成功,但是最后由于写作模块的 Markdown 编辑器难以实现而中途放弃,主要问题在于前端视图频频出粗无法呈现想要的视图。先把这个完成一半的项目保存下来,现阶段的主要任务还是安全方向的学习,之后有时间再继续研究。

自研动态系统建站失败后,我把目光投向了 WordPress 建站这一简单不耗时的建站方法,同时还找到了一个很优秀的主题 Argon

Argon 具有美观的前端界面和较强的自定义功能,且对于代码能力要求较低,原则上能实现无代码建站。Wordpress 的后台管理功能也比较完善。

于是乎就有了现在的这个博客网站。

Sakurairo