Python 30 天进阶:继续完善个人博客网站
在前一篇搭建起个人博客网站的基础框架后,今天我们要进一步完善它,从优化前端页面设计入手,融入 JavaScript 来增添交互功能,同时完善博客文章的分类和标签相关功能,让这个博客网站更加实用且用户体验更佳。
一、前端页面优化与 JavaScript 交互功能实现
(一)页面样式优化
可以使用更丰富的 CSS 属性和框架(如 Bootstrap 等,这里以原生 CSS 继续拓展为例)来进一步美化页面。例如,调整文章列表的排版,让文章标题和简介展示得更清晰美观:
article {
border: 1px solid #ccc;
border-radius: 5px;
padding: 15px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
article h2 {
margin-top: 0;
}
article p {
margin-bottom: 0;
}
通过上述 CSS 代码,为每篇文章添加了边框、圆角、阴影等样式,让文章展示更具层次感和立体感,同时对文章内的标题和段落元素做了一些间距调整。
(二)文章点赞功能(结合 JavaScript 与后端交互)
- 后端 API 设计(Flask 路由):
首先在后端创建用于处理点赞操作的路由,示例代码如下:
@app.route('/article/like/<int:article_id>', methods=['POST'])
def like_article(article_id):
article = db_session.query(Article).filter_by(id=article_id).first()
if article:
# 这里简单模拟点赞数加 1,实际可根据需求完善逻辑,比如记录点赞用户等
article.likes += 1
db_session.commit()
return jsonify({'success': True, 'likes': article.likes})
return jsonify({'success': False})
在上述代码中,定义了一个接收文章 id 的 POST 路由,用于处理点赞请求。当接收到请求后,从数据库中查找对应的文章,如果找到则增加文章的点赞数(假设 Article 类中新增了 likes 属性用于记录点赞数),提交事务后返回包含操作成功状态以及当前点赞数的 JSON 数据,若文章不存在则返回操作失败的 JSON 数据。
- 前端 JavaScript 实现:
在文章详情页面(article_detail.html)中引入 JavaScript 代码来发送点赞请求,示例如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>个人博客 - 文章详情</title>
<link rel="stylesheet" type="text/css" href="styles.css">
<script>
function likeArticle(articleId) {
fetch(`/article/like/${articleId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById('like-count').innerText = `点赞数: ${data.likes}`;
} else {
alert('点赞失败,请稍后再试');
}
});
}
</script>
</head>
<body>
<h1>{{ article.title }}</h1>
<div class="article-content">
{{ article.content }}
</div>
<button onclick="likeArticle({{ article.id }})">点赞</button>
<span id="like-count">点赞数: {{ article.likes }}</span>
</body>
</html>
在上述 HTML 页面中,添加了一个 script 标签,里面定义了 likeArticle 函数,该函数使用 fetch API 发送 POST 请求到后端的点赞路由,传递文章 id 参数。根据后端返回的 JSON 数据,若点赞成功则更新页面上显示的点赞数,若失败则弹出提示框告知用户。同时,在页面中添加了点赞按钮,并绑定 likeArticle 函数,传递当前文章的 id 值,还显示了初始的点赞数。
(三)评论功能(前后端交互实现)
- 数据库表结构设计(新增评论表):
class Comment(Base):
__tablename__ = 'comments'
id = Column(Integer, primary_key=True)
article_id = Column(Integer, ForeignKey('articles.id'))
commenter_name = Column(String(50), nullable=False)
comment_content = Column(Text, nullable=False)
comment_time = Column(DateTime, default=func.now())
Base.metadata.create_all(engine)
上述代码定义了 Comment 类对应数据库中的 comments 表,包含评论的唯一标识 id、关联的文章 id(通过外键与 articles 表关联)、评论者姓名、评论内容以及评论时间等字段,这样就能记录每篇文章的相关评论信息了。
- 后端路由与评论处理逻辑:
@app.route('/article/comment/<int:article_id>', methods=['POST'])
def add_comment(article_id):
data = request.get_json()
commenter_name = data.get('commenter_name')
comment_content = data.get('comment_content')
new_comment = Comment(article_id=article_id, commenter_name=commenter_name, comment_content=comment_content)
db_session.add(new_comment)
db_session.commit()
return jsonify({'success': True})
这里定义了处理文章评论添加的路由,接收文章 id 以及从前端发送过来的 JSON 格式的评论者姓名和评论内容数据,创建 Comment 对象并添加到数据库,提交事务后返回操作成功的 JSON 数据。
- 前端 JavaScript 实现评论提交与展示(简单示例):
在文章详情页面添加以下 JavaScript 代码用于评论相关功能:
<script>
function addComment(articleId) {
const commenterName = document.getElementById('commenter-name').value;
const commentContent = document.getElementById('comment-content').value;
fetch(`/article/comment/${articleId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
commenter_name: commenterName,
comment_content: commentContent
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 这里简单模拟添加评论后刷新页面来展示新评论,实际可优化为局部更新
location.reload();
} else {
alert('评论发布失败,请稍后再试');
}
});
}
</script>
...
<div>
<input type="text" id="commenter-name" placeholder="请输入您的姓名">
<textarea id="comment-content" placeholder="请输入您的评论"></textarea>
<button onclick="addComment({{ article.id }})">发布评论</button>
</div>
在上述代码中,定义了 addComment 函数,用于获取用户在输入框中输入的评论者姓名和评论内容,然后通过 fetch 发送 POST 请求到后端评论添加路由,传递相应数据。根据后端返回的结果,若成功则刷新页面展示新评论(可优化为只局部更新评论区域),若失败则弹出提示框告知用户。同时在页面中添加了输入框和按钮等元素用于用户输入和提交评论。
(四)页面动态加载效果(使用 JavaScript)
例如,在文章列表页面实现当滚动到底部时动态加载更多文章的效果,示例 JavaScript 代码如下(需要结合后端分页查询文章的功能,这里先简单展示前端思路):
<script>
window.addEventListener('scroll', function () {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
// 这里发送请求获取下一页文章数据,可通过调用后端分页查询的 API
// 假设后端有 /articles?page=2 这样的路由用于获取第二页文章
fetch('/articles?page=2')
.then(response => response.json())
.then(data => {
const articleList = document.getElementById('article-list');
data.articles.forEach(article => {
const articleElement = document.createElement('article');
articleElement.innerHTML = `<h2>${article.title}</h2><p>${article.content}</p>`;
articleList.appendChild(articleElement);
});
});
}
});
</script>
...
<body>
<h1>我的博客文章列表</h1>
<ul id="article-list">
{% for article in articles %}
<li><a href="{{ url_for('article_detail', article_id=article.id) }}">{{ article.title }}</a></li>
{% endfor %}
</ul>
</body>
在上述代码中,通过监听页面的 scroll 事件,当滚动到页面底部(通过判断滚动高度和窗口高度与页面总高度的关系)时,发送请求到后端获取下一页文章数据(这里假设后端有相应的分页查询路由),然后将获取到的文章数据动态创建 HTML 元素并添加到文章列表中,实现动态加载更多文章的效果。
二、博客文章的分类和标签功能完善
(一)数据库表结构设计
- 分类表(Category):
class Category(Base):
__tablename__ = 'categories'
id = Column(Integer, primary_key=True)
category_name = Column(String(50), nullable=False)
Base.metadata.create_all(engine)
定义了 Category 表用于存储文章的分类信息,包含分类的唯一标识 id 和分类名称 category_name 字段。
- 标签表(Tag):
class Tag(Base):
__tablename__ = 'tags'
id = Column(Integer, primary_key=True)
tag_name = Column(String(50), nullable=False)
Base.metadata.create_all(engine)
Tag 表用于存储文章的标签信息,同样包含 id 和 tag_name 字段。
- 文章与分类、标签关联表(ArticleCategory、ArticleTag):
class ArticleCategory(Base):
__tablename__ = 'article_categories'
article_id = Column(Integer, ForeignKey('articles.id'), primary_key=True)
category_id = Column(Integer, ForeignKey('categories.id'), primary_key=True)
class ArticleTag(Base):
__tablename__ = 'article_tags'
article_id = Column(Integer, ForeignKey('articles.id'), primary_key=True)
tag_id = Column(Integer, ForeignKey('tags.id'), primary_key=True)
Base.metadata.create_all(engine)
通过这两个关联表,建立了文章与分类、文章与标签之间多对多的关系,方便后续进行分类筛选和标签筛选展示文章的操作。
(二)后端路由与数据查询逻辑(以分类筛选为例)
@app.route('/category/<int:category_id>')
def articles_by_category(category_id):
articles = db_session.query(Article).join(ArticleCategory).filter(ArticleCategory.category_id == category_id).all()
return render_template('article_list.html', articles=articles)
上述代码定义了根据分类 id 筛选文章的路由,通过数据库的 join 操作,基于 ArticleCategory 关联表,筛选出属于指定分类的文章,然后将这些文章传递给文章列表模板进行展示,实现分类筛选文章的功能。
(三)前端页面实现分类和标签筛选展示(简单示例)
在文章列表页面添加分类和标签筛选的 HTML 元素及 JavaScript 交互代码,示例如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>个人博客 - 文章列表</title>
<link rel="stylesheet" type="text/css" href="styles.css">
<script>
function filterByCategory(categoryId) {
window.location.href = `/category/${categoryId}`;
}
function filterByTag(tagId) {
// 类似分类筛选,需后端实现根据标签筛选文章的路由,这里暂省略
window.location.href = `/tag/${tagId}`;
}
</script>
</head>
<body>
<h1>我的博客文章列表</h1>
<div>
<h3>分类筛选:</h3>
{% for category in categories %}
<button onclick="filterByCategory({{ category.id }})">{{ category.category_name }}</button>
{% endfor %}
</div>
<div>
<h3>标签筛选:</h3>
{% for tag in tags %}
<button onclick="filterByTag({{ tag.id }})">{{ tag.tag_name }}</button>
{% endfor %}
</div>
<ul>
{% for article in articles %}
<li><a href="{{ url_for('article_detail', article_id=article.id) }}">{{ article.title }}</a></li>
{% endfor %}
</ul>
</body>
</html>
在上述页面中,通过 Jinja2 模板引擎遍历从后端传递过来的分类和标签列表,为每个分类和标签生成一个按钮,点击按钮时调用相应的 JavaScript 函数,函数内通过修改 window.location.href 将页面跳转到后端对应的分类或标签筛选路由(后端标签筛选路由暂未完整展示,可参照分类筛选路由进行实现),从而实现根据分类和标签筛选展示文章的功能。
通过今天对个人博客网站的进一步完善,我们添加了诸多实用且有趣的交互功能,同时完善了文章分类和标签相关的功能,让博客网站更加符合实际使用场景,用户体验也得到了显著提升。后续还可以继续优化性能、添加更多功能,如用户权限管理、文章搜索功能等。
#个人博客完善# #Web 应用拓展# #前端交互功能# #数据库设计优化# #博客分类标签# #Python Web 编程进阶#