在刚接触到 web 时,我就产生了亲手搭建一个网站的想法。在之后的学习中慢慢了解到了个人博客的概念,也就决定了要搭建一个属于自己的博客。历经一年,终于有了一定的知识储备,当时的想法有机会付诸实践了,我便抽出了几天的时间想要尝试一下。
下面具体记录一下我在这次博客搭建中学到的东西。
静态博客与动态博客
静态博客
- 所有的文章页面在发布前就已经生成成了固定的 HTML 文件。
- 浏览者访问时,服务器只是直接把这些 HTML 文件发给浏览器,没有再去运行程序或查询数据库。
- 常用工具:Hexo、Hugo、Jekyll 等静态网站生成器。
动态博客
- 页面不是事先生成的,而是访问时临时拼出来的。
- 服务器会先运行代码(如 Python、PHP、Node.js 等),从数据库读取文章内容,再把 HTML 返回给浏览器。
我理想中的个人博客是动态博客,相对于静态博客来说在发布文章、管理网站和功能实现上都更占优势。
自研动态博客系统
本质上就是自己写一个小型的 CMS(内容管理系统)。
建站方式:Flask + Jinja2 + SQLite
数据库选用 SQLite 的原因
- SQLite 不需要像 MySQL 或 PostgreSQL 那样进行复杂的安装和配置。数据库的创建仅仅依赖于一个文件。只需要提供数据库文件路径,SQLite 就能立即工作。
- SQLite 不需要专门的数据库管理员。只要能够访问数据库文件,任何地方都可以操作数据库,非常适合小型项目或个人开发者使用。
- 对于小型应用,SQLite 提供了非常好的性能。在数据量较小或并发较低时,它的数据读写速度很快。
网站结构
采用 Flask 工厂模式结构建站
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主要搭建逻辑
- 项目初始化和环境准备
- 创建并激活虚拟环境,安装 Flask 及基础依赖
- 创建项目文件夹结构
- 初始化 Flask 应用工厂
app/__init__.py中创建create_app()函数- 编写
config.py,定义基础配置(如数据库 URI,密钥等) - 编写
app/extensions.py,创建并初始化扩展对象 app/__init__.py中加载配置,初始化 Flask 扩展
- 运行入口
- 编写
run.py,导入create_app()创建应用实例
- 编写
- 数据库模型设计
- 编写
app/models.py,设计文章、用户、评论等模型 - 定义模型关系
- 初始化迁移:
flask db init创建迁移脚本:flask db migrate应用迁移:flask db upgrade
- 编写
- 视图蓝图框架搭建
app/templates/index.html编写首页模板app/views/main.py先写首页路由,返回简单的模板测试渲染app/views/__init__.py导入视图模块,方便注册app/__init__.py注册蓝图
- 其他
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
<!--文章板块视图-->
{% 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>
<!--分页导航栏视图结束-->HTMLblueprint.py
@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)PythonArgon
自研动态系统建站本来已经有了一定的成功,但是最后由于写作模块的 Markdown 编辑器难以实现而中途放弃,主要问题在于前端视图频频出粗无法呈现想要的视图。先把这个完成一半的项目保存下来,现阶段的主要任务还是安全方向的学习,之后有时间再继续研究。
自研动态系统建站失败后,我把目光投向了 WordPress 建站这一简单不耗时的建站方法,同时还找到了一个很优秀的主题 Argon。
Argon 具有美观的前端界面和较强的自定义功能,且对于代码能力要求较低,原则上能实现无代码建站。Wordpress 的后台管理功能也比较完善。
于是乎就有了现在的这个博客网站。

Comments NOTHING