Python学习之路-Flask项目:博客前台

Python学习之路-Flask项目:博客前台

前言

前一篇完成了项目开发前的所有准备工作,本篇将完成整个博客的前台相关工作。

首页

前言

首页主要为我们提供各个分类入口已经按时间顺序的文章列表和列表排行等数据,是进入博客中第一眼看到的,接下来就来实现吧。

排行展示

前言

在请求根路由时去数据库查询按点击量排行的10条文章

后端实现
@index_blu.route('/')
def index():...# 获取点击排行数据blog_list = Nonetry:blog_list = Blog.query.order_by(Blog.clicks.desc()).limit(constants.CLICK_RANK_MAX_BLOG)except Exception as e:current_app.logger.error(e)click_blog_list = []for blog in blog_list if blog_list else []:click_blog_list.append(blog.to_basic_dict())data = {"user_blog": user.to_dict() if user else None,"click_blog_list": click_blog_list,}return render_template('blog/index.html', data=data)

分类展示

前言

分类展示是将博客按我们想要的分类进行分割的一个关键部分,需要我们通过分类将其展示出来。

后端实现

在请求根路由的时候去查询博客分类,并默认设置第1个分类选中

@index_blu.route('/')
def index():...# 获取博客分类数据categories = Category.query.all()# 定义列表保存分类数据categories_dicts = []for category in enumerate(categories):# 拼接内容categories_dicts.append(category.to_dict())data = {"user_blog": user.to_dict() if user else None,"click_blog_list": click_blog_list,"categories": categories_dicts}return render_template('blog/index.html', data=data)

文章列表

前言

文章列表是整个博客最重要的一部分,当我们点击分类时需要去获取当前分类下的文章数据,当我们点击标签时需要去获取当前标签下的文章数据。在展示的时候需要更新文章列表界面,不需要整体页面刷新,所以文章数据也使用 ajax 的方式去请求后台接口进行获取。

接口设计
  • URL:/blog_list
  • 请求方式:GET
  • 传入参数:JSON格式
  • 参数
参数名类型是否必须参数说明
cidstring分类id
pageint页数,不传即获取第1页
per_pageint每页多少条数据,如果不传,默认10条
  • 返回类型:JSON
参数名类型是否必须参数说明
errnoint错误码
errmsgstring错误信息
cidstring当前文章数据的分类id
totalPageint总页数
currentPageint当前页数
blogListlist列表数据
blogList.titlestring标题
blogList.sourcestring来源
blogList.digeststring摘要
blogList.create_timestring时间
blogList.index_image_urlstring索引图
后端实现
  • index/views.py中定义视图函数。在查询的时候,如果用户分类id传0,则不添加分类查询条件
@index_blu.route('/bloglist')
def get_blog_list():"""获取指定分类的博客列表:return: 包含文章列表的jsonify对象"""# 1. 获取参数args_dict = request.argspage = args_dict.get("p", '1')per_page = args_dict.get("per_page", constants.HOME_PAGE_MAX_BLOG)category_id = args_dict.get("cid", '1')# 2. 校验参数try:page = int(page)per_page = int(per_page)except Exception as e:current_app.logger.error(e)return jsonify(errno=RET.PARAMERR, errmsg="参数错误")# 3. 查询数据并分页filters = []# 如果分类id不为1,那么添加分类id的过滤if category_id != "1":filters.append(Blog.category_id == category_id)try:paginate = Blog.query.filter(*filters).order_by(Blog.create_time.desc()).paginate(page, per_page, False)# 获取查询出来的数据items = paginate.items# 获取到总页数total_page = paginate.pagescurrent_page = paginate.pageexcept Exception as e:current_app.logger.error(e)return jsonify(errno=RET.DBERR, errmsg="数据查询失败")blog_list = []for blog in items:blog_list.append(blog.to_basic_dict())# 4. 返回数据return jsonify(errno=RET.OK, errmsg="OK", totalPage=total_page, currentPage=current_page, blogList=blog_list, cid=category_id)

详情页

前言

博客详情页是整个博客的精髓所在,我们需要完成:基类模板抽取、使用装饰器的形式完成用户数据的查询并传递到视图函数中、查询博客详情页数据并使用模板语言进行数据的展示、写出文章评论的后端逻辑、写出文章点赞的后端逻辑等功能。

准备工作

博客详情页面的前端页面为 blog/detail.html,创建博客详情页的模块blog,并在该模块下创建视图函数存储的 py 文件 views.py

  • 创建蓝图

    from flask import Blueprintblog_blu = Blueprint("blog", __name__, url_prefix='/blog')from . import views
    
  • 注册蓝图

    def create_app(config_name):...# 注册蓝图from blog.modules.index import index_bluapp.register_blueprint(index_blu)from blog.modules.passport import passport_bluapp.register_blueprint(passport_blu)from blog.modules.blog import blog_bluapp.register_blueprint(blog_blu)...return app
    
  • 提供文章详情页访问的视图函数,将文章详情页界面移动到模板文件夹,视图函数需要接受文章 id 作为参数,以便后续查询文章详情数据

    @blog_blu.route('/<int:blog_id>')
    def blog_detail(blog_id):return render_template('blog/detail.html')

数据展示

文章内容数据,查询文章数据并返回

@blog_blu.route('/<int:blog_id>')
@user_login_data
def blog_detail(blog_id):try:blog = blog.query.get(blog_id)except Exception as e:current_app.logger.error(e)abort(404)if not blog:# 返回数据未找到的页面abort(404)blog.clicks += 1data = {"blog": blog.to_dict(),"user_blog": g.user.to_dict() if g.user else None,}return render_template('blog/detail.html', data=data)

文章排行

  • 查询文章点击量数据,并返回
@blog_blu.route('/<int:blog_id>')
@user_login_data
def blog_detail(blog_id):...# 获取点击排行数据blog_list = Nonetry:blog_list = blog.query.order_by(blog.clicks.desc()).limit(constants.CLICK_RANK_MAX_blog)except Exception as e:current_app.logger.error(e)click_blog_list = []for blog in blog_list if blog_list else []:click_blog_list.append(blog.to_basic_dict())data = {"blog": blog.to_dict(),"click_blog_list": click_blog_list,"user_blog": g.user.to_dict() if g.user else None,}return render_template('blog/detail.html', data=data)\

文章评论

需求分析

用户如果在登录的情况下,可以进行评论,未登录,点击评论弹出登录框。用户可以直接评论当前文章

后端实现
接口设计
  • URL:/blog/blog_comment
  • 请求方式:POST
  • 传入参数:JSON格式
  • 参数
参数名类型是否必须参数说明
blog_idint文章id
commentstring评论内容
parent_idint回复的评论的id
  • 返回类型:JSON
参数名类型是否必须参数说明
errnoint错误码
errmsgstring错误信息
代码实现

blog/views.py 文件中添加评论文章的视图函数

@blog_blu.route('/blog_comment', methods=["POST"])
@user_login_data
def add_blog_comment():"""添加评论"""user = g.userif not user:return jsonify(errno=RET.SESSIONERR, errmsg="用户未登录")# 获取参数data_dict = request.jsonblog_id = data_dict.get("blog_id")comment_str = data_dict.get("comment")parent_id = data_dict.get("parent_id")if not all([blog_id, comment_str]):return jsonify(errno=RET.PARAMERR, errmsg="参数不足")try:blog = blog.query.get(blog_id)except Exception as e:current_app.logger.error(e)return jsonify(errno=RET.DBERR, errmsg="查询数据失败")if not blog:return jsonify(errno=RET.NODATA, errmsg="该文章不存在")# 初始化模型,保存数据comment = Comment()comment.user_id = user.idcomment.blog_id = blog_idcomment.content = comment_strif parent_id:comment.parent_id = parent_id# 保存到数据库try:db.session.add(comment)db.session.commit()except Exception as e:current_app.logger.error(e)return jsonify(errno=RET.DBERR, errmsg="保存评论数据失败")# 返回响应return jsonify(errno=RET.OK, errmsg="评论成功", data=comment.to_dict())
文章评论列表

在文章详情的视图函数中,添加查询当前文章评论的逻辑

@blog_blu.route('/<int:blog_id>')
@user_login_data
def blog_detail(blog_id):...# 获取当前文章的评论comments = []try:comments = Comment.query.filter(Comment.blog_id == blog_id).order_by(Comment.create_time.desc()).all()except Exception as e:current_app.logger.error(e)comment_list = []for item in comments:comment_dict = item.to_dict()comment_list.append(comment_dict)is_collected = False# 判断用户是否收藏过该文章if g.user:if blog in g.user.collection_blog:is_collected = Truedata = {"blog": blog.to_dict(),"click_blog_list": click_blog_list,"is_collected": is_collected,"user_blog": g.user.to_dict() if g.user else None,"comments": comment_list}return render_template('blog/detail.html', data=data)
评论点赞功能
需求分析
  • 后端提供点赞和取消点赞功能
  • 当用户点击未点赞按钮,执行点赞逻辑,向后端发起点赞请求,取消点赞则反之
  • 在文章显示完成之后,底部评论会根据当前登录用户显示是否点赞图标
接口设计
  • URL:/blog/comment_like
  • 请求方式:POST
  • 传入参数:JSON格式
  • 参数
参数名类型是否必须参数说明
comment_idint评论id
blog_idint文章id
actionstring点赞操作类型:add(点赞),remove(取消点赞)
  • 返回类型:JSON
参数名类型是否必须参数说明
errnoint错误码
errmsgstring错误信息
后端代码实现
  • blog/views.py 中添加点赞/取消点赞视图函数
@blog_blu.route('/comment_like', methods=["POST"])
@user_login_data
def set_comment_like():"""评论点赞"""if not g.user:return jsonify(errno=RET.SESSIONERR, errmsg="用户未登录")# 获取参数comment_id = request.json.get("comment_id")blog_id = request.json.get("blog_id")action = request.json.get("action")# 判断参数if not all([comment_id, blog_id, action]):return jsonify(errno=RET.PARAMERR, errmsg="参数错误")if action not in ("add", "remove"):return jsonify(errno=RET.PARAMERR, errmsg="参数错误")# 查询评论数据try:comment = Comment.query.get(comment_id)except Exception as e:current_app.logger.error(e)return jsonify(errno=RET.DBERR, errmsg="查询数据失败")if not comment:return jsonify(errno=RET.NODATA, errmsg="评论数据不存在")if action == "add":comment_like = CommentLike.query.filter_by(comment_id=comment_id, user_id=g.user.id).first()if not comment_like:comment_like = CommentLike()comment_like.comment_id = comment_idcomment_like.user_id = g.user.iddb.session.add(comment_like)# 增加点赞条数comment.like_count += 1else:# 删除点赞数据comment_like = CommentLike.query.filter_by(comment_id=comment_id, user_id=g.user.id).first()if comment_like:db.session.delete(comment_like)# 减小点赞条数comment.like_count -= 1try:db.session.commit()except Exception as e:current_app.logger.error(e)db.session.rollback()return jsonify(errno=RET.DBERR, errmsg="操作失败")return jsonify(errno=RET.OK, errmsg="操作成功")

404 页面

需求

在用户访问一些不存在网址的时候弹出404页面

实现逻辑

可以使用 app.errorhandle(code_or_exception) 装饰器

代码实现

  • static/blog/404.html 文件拖到 templates/blog/ 目录下,并继承于基类模板

  • blog/__init__.py 文件中的 create_app 函数中添加以下逻辑

def create_app(config_name):...@app.errorhandler(404)@user_login_datadef page_not_found(_):user = g.userdata = {"user_blog": user.to_dict() if user else None}return render_template('blog/404.html', data=data)return app

在浏览器里面输入网址进行测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/645657.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【JAVA语言-第15话】集合框架(二)——List、ArrayList、LinkedList、Vector集合

目录 List集合 1.1 概述 1.2 特点 1.3 常用方法 1.4 ArrayList集合 1.4.1 概述 1.4.2 练习 1.5 LinkedList集合 1.5.1 概述 1.5.2 特点 1.5.3 常用方法 1.5.4 练习 1.6 Vector类 1.6.1 概述 1.6.2 练习 1.7 List实现类的异同点 List集合 1.1 概述 java.util…

嵌入式培训机构四个月实训课程笔记(完整版)-Linux ARM平台编程第三天-Bootloader编写(物联技术666)

链接&#xff1a;https://pan.baidu.com/s/1KE2cq_kHaRW5HsP29hgL6w?pwd1688 提取码&#xff1a;1688 链接脚本 0. Contents 1. 概论 2. 基本概念 3. 脚本格式 4. 简单例子 5. 简单脚本命令 6. 对符号的赋值 7. SECTIONS命令 8. MEMORY命令 9. PHDRS命令 10. VERS…

函数式接口

文章目录 函数式接口函数式接口当做方法的参数函数式接口作为返回值生产者接口 - Supplier消费者接口 - Consumer使用方式&#xff1a;案例&#xff1a;字符串拆分 判断接口 - Predicate使用方式应用场景 加工接口 - Function 函数式接口 只有一个抽象方法的接口&#xff0c;转…

多旋翼无人机调试问题分析

一、电机和螺旋桨检查 在多旋翼无人机的调试过程中&#xff0c;首先需要检查电机和螺旋桨的状态。电机应转动灵活&#xff0c;无卡滞现象&#xff0c;且无明显磨损。螺旋桨应安装牢固&#xff0c;无松动现象&#xff0c;且桨叶完好无损。若发现问题&#xff0c;应及时更换或维…

(菜鸟自学)漏洞利用——MS11-080

&#xff08;菜鸟自学&#xff09;漏洞利用——MS11-080 漏洞简介利用漏洞对系统进行提权查看漏洞利用代码和工具将py脚本转换为exe程序渗透攻击验证 漏洞简介 MS11-080 是指微软于 2011 年发布的一个安全公告&#xff08;MS11-080&#xff09;&#xff0c;其中包含了关于 Win…

自动化网络故障管理

故障管理是网络管理的组成部分&#xff0c;涉及检测、隔离和解决问题&#xff0c;如果实施得当&#xff0c;网络故障管理可以使连接、应用程序和服务保持在最佳水平&#xff0c;提供容错能力并最大限度地减少停机时间&#xff0c;专门为此目的设计的平台或工具称为故障管理系统…

【并发编程】顺序控制交替输出abc

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;并发编程⛺️稳重求进&#xff0c;晒太阳 必须先2后1打印 用synchronized package aaa;public class Test2 {static Boolean hasExecutorfalse;public static void main(String[] args) …

1 - 搭建Redis数据库服务器|LNP+Redis

搭建Redis数据库服务器&#xff5c;LNPRedis 搭建Redis数据库服务器相关概念Redis介绍安装RedisRedis服务常用管理命令命令set 、 mset 、 get 、 mget命令keys 、 type 、 exists 、 del命令ttl 、 expire 、 move 、 flushdb 、flushall 、save、shutdown 配置文件解析 LNP …

Wordpress seo优化该怎么做?

Wordpress作为开源管理系统&#xff0c;目前已然是世界上最流行的cms之一&#xff0c;这不仅仅因为他开源&#xff0c;对用户友好&#xff0c;让任何人都能轻而易举的制作网站&#xff0c;更是因为这套程序对于搜索引擎非常友好&#xff0c;是做谷歌seo的不二之选 Wordpress作为…

【Ubuntu】systemctl 命令

systemctl 是一个用于检视和控制 systemd 系统和服务管理器的命令行工具。systemd 是用于启动、停止和管理 Linux 系统中的服务的守护进程。以下是一些常用的 systemctl 命令及其说明&#xff1a; systemctl start <unit>: 启动指定的服务单元 systemctl start sshd sy…

【腾讯云服务器】幻兽帕鲁私服服务器部署保姆级教程

在帕鲁的世界&#xff0c;你可以选择与神奇的生物「帕鲁」一同享受悠闲的生活&#xff0c;也可以投身于与偷猎者进行生死搏斗的冒险。帕鲁可以进行战斗、繁殖、协助你做农活&#xff0c;也可以为你在工厂工作。你也可以将它们进行售卖&#xff0c;或分解后食用。 想要部署属于自…

Java入门篇:打造你的Java开发环境——从零开始配置IDEA与Eclipse

引言 “工欲善其事&#xff0c;必先利其器” 作为每一位Java初学者的必经之路&#xff0c;搭建合适的开发环境是至关重要的第一步。本篇将详细指导你如何安装并配置两大主流Java开发工具——IntelliJ IDEA和Eclipse&#xff0c;助你在编程之旅上迈出坚实的第一步。 一、Java开发…

微信小程序分页加载功能,结合后端实现上拉底部加载下一页数据,数据加载中和暂无数据提示

&#x1f935; 作者&#xff1a;coderYYY &#x1f9d1; 个人简介&#xff1a;前端程序媛&#xff0c;目前主攻web前端&#xff0c;后端辅助&#xff0c;其他技术知识也会偶尔分享&#x1f340;欢迎和我一起交流&#xff01;&#x1f680;&#xff08;评论和私信一般会回&#…

【服务器Midjourney】Midjourney网站0基础搭建

目录 🌺【前言】 🌺【准备】 🌺【宝塔搭建MJ】 🌼1. 给服务器添加端口 🌼2. 使用Xshell连接服务器 🌼3. 安装docker 🌼4. 安装Midjourney程序 🌼5. 绑定域名+申请SSL证书 🌼6. 更新网站

深入理解Flutter中的GlobalKey与LocalKey(ValueKey、ObjectKey、UniqueKey)及其使用方法

在Flutter中&#xff0c;Key是一个非常重要的概念&#xff0c;它用于标识和管理Widget。GlobalKey和LocalKey是Key的两个主要子类&#xff0c;而ValueKey、ObjectKey和UniqueKey则是LocalKey的具体实现。在本文中&#xff0c;我们将深入介绍这些关键概念以及它们在Flutter中的使…

XR虚拟拍摄技术:开启短剧与微剧的全新篇章

随着科技的快速发展&#xff0c;XR虚拟拍摄技术已经成为短剧与微剧制作的重要工具。这种技术为影视制作带来了巨大的变革&#xff0c;为观众带来了更加丰富、逼真的视听体验。 XR虚拟拍摄技术通过构建虚拟场景&#xff0c;使得制作人员能够更加自由地发挥创意和想象力。这种技术…

android:excludeFromRecents

android:excludeFromRecents 基础从根上影响 TaskexcludeFromRecents 属性可能会影响系统 基础 android:excludeFromRecents是一种在Android应用程序清单文件&#xff08;AndroidManifest.xml&#xff09;中使用的属性&#xff0c;用于指定一个Activity是否应该在最近任务列表…

两数之和[中等]

一、题目 给你一个下标从1开始的整数数组numbers&#xff0c;该数组已按非递减顺序排列&#xff0c;请你从数组中找出满足相加之和等于目标数target的两个数。如果设这两个数分别是numbers[index1]和numbers[index2]&#xff0c;则1 < index1 < index2 < numbers.len…

MySQL修炼手册17:高级查询优化:深入理解执行计划

目录 写在开头1 执行计划的基本概念1.1 SQL查询解析1.1.1 语法分析1.1.2 语义分析1.1.3 内部数据结构的生成1.2 优化执行计划1.2.1 统计信息的利用1.2.2 索引选择1.2.3 连接算法1.2.4 子查询优化1.3 执行计划的评估1.3.1 统计信息的利用1.3.2 索引选择1.3.3 连接算法1.3.4 子查…

关于视觉3d目标检测学习像素深度的一点理解

在真实世界的一个物体&#xff0c;可以通过相机矩阵将其投影到像素坐标系上 但是&#xff0c;在像素坐标系上的像素&#xff0c;由于相机的原理&#xff0c;导致它的深度信息已经没有了&#xff0c;所以原理上是没法得到其真实深度的(即3d位置) 那么现在的深度学习方法又为什…