https://gitee.com/cz_zzz/InfoNews24
index.html:
{% for news in rank_list %}
- {{ loop.index }}{{ news.title }}
{% endfor %}
views.py:
# 查询 点击量排行前10的新闻
try:
rank_list = News.query.order_by(News.clicks.desc()).limit(10).all()
except BaseException as e:
current_app.logger.error(e)
return abort(500)
user = user.to_dict() if user else None
# 将新闻排行数据 传入模板渲染
return render_template("index.html", user=user, rank_list=rank_list)
index.html:
{{ loop.index }}{{ news.title }}
common.py:
# 定义自定义过滤器
def func_index_convert(index): # 1.定义形参接收模板变量
index_dict = {1: "first", 2: 'second', 3: "third"}
return index_dict.get(index, "") # 2.将转换结果返回
init.py:
# 让应用添加过滤器
from info.utils.common import func_index_convert
app.add_template_filter(func_index_convert, "index_convert")
接口分析
渲染方式:局部刷新–>前端渲染–>定义路由,返回数据
数据库行为:按照分类和页码来查询(get)新闻 按发布时间倒叙排列
接口文档
获取新闻列表
/get_news_list(新建)
请求方式
GET
请求参数
cid 分类id
cur_page 当前页码
per_count 每页条数
响应形式
json
# 获取新闻列表
@home_blu.route('/get_news_list')
def get_news_list():
# 获取参数
cid = request.args.get("cid")
cur_page = request.args.get("cur_page")
per_count = request.args.get("per_count", HOME_PAGE_MAX_NEWS)
# 校验参数
if not all([cid, cur_page]):
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])
try:
cid = int(cid)
cur_page = int(cur_page)
per_count = int(per_count)
except BaseException as e:
current_app.logger.error(e)
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])
# 数据库行为: 按照分类和页码查询新闻 按发布时间倒序
try:
pn = News.query.filter_by(category_id=cid).order_by(News.create_time.desc()).paginate(cur_page, per_count)
except BaseException as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])
data = {
"news_list": [news.to_dict() for news in pn.items],
"total_page": pn.pages # 前端需要根据总页数来判断是否还需要请求
}
# 返回json结果
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=data)
tips:
"news_list": [news.to_dict() for news in pn.items],
# 数据库行为: 按照分类和页码查询新闻 按发布时间倒序
try: # 如果是"最新", 所有新闻一起分页排序
if cid == 1:
pn = News.query.order_by(News.create_time.desc()).paginate(cur_page, per_count)
else: # 如果为其他分类, 根据cid进行筛选
pn = News.query.filter_by(category_id=cid).order_by(News.create_time.desc()).paginate(cur_page, per_count)
except BaseException as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])
# 数据库行为: 按照分类和页码查询新闻 按发布时间倒序
filter_list = []
if cid != 1:
filter_list.append(News.category_id==cid) # filter_list = [News.category_id==cid]
try: # 如果是"最新", 所有新闻一起分页排序
pn = News.query.filter(*filter_list).order_by(News.create_time.desc()).paginate(cur_page, per_count)
# if cid == 1:
# pn = News.query.order_by(News.create_time.desc()).paginate(cur_page, per_count)
# else: # 如果为其他分类, 根据cid进行筛选
# pn = News.query.filter(News.category_id==cid).order_by(News.create_time.desc()).paginate(cur_page, per_count)
except BaseException as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])
tips:注释部分为原来思路,本次考虑用实参
前面加*–>类似于解包---->如果没有值,则结果是""(空字符串)
接口分析:
渲染方式: 全局刷新–>后端
渲染–> 跟路由
数据库行为: 查询
分类数据
demo实现
# 查询所有的分类数据
try:
categories = Category.query.all()
except BaseException as e:
current_app.logger.error(e)
return abort(500)
user = user.to_dict() if user else None
# 将新闻排行数据 和 分类数据 传入模板渲染
return render_template("index.html", user=user, rank_list=rank_list, categories=categories)
tips:注意要在return
处返回categories=categories
tips:data-cid="{{ category.id }}"
用于构建获取新闻列表的请求参数
接口分析
渲染方式:全局刷新/SEO–>后端
渲染—>新定义路由(modules-->views
),渲染详情页面
数据库行为:查询
某条新闻数据—>前端需要传递新闻的id
接口文档
新闻详情
/news/动态URL传递新闻id
的方式
请求方式
GET
请求参数
无
响应形式
html
tips
:渲染方式---->响应形式;数据库的行为----->请求参数---->请求方式
代码实现
views界面:
from flask import current_app, abort, render_template
from info.modules.news import news_blu
from info.utils.models import News
@news_blu.route('/') # 新闻详情
def news_detail(news_id):
# 根据新闻id查询新闻模型
try:
news = News.query.get(news_id)
except BaseException as e:
current_app.logger.error(e)
return abort(500)
# 将新闻数据传入模板渲染
return render_template("detail.html", news=news.to_dict())
模板渲染
{{ news.title }}
{{ news.create_time }} 来源: {{ news.source }}
0
{{ news.content | safe }} {# jinja2对html字符进行自动转义,可以使用safe取消转义 #}
tips:
由于home中的首页数据传到了index.html进行渲染,而基类里并没有提留存
故:
在详情页(news)的views要对detail进行传参进行渲染
装饰器bug
1. flask中不允许同一个蓝图定义的路由的函数标记相同,一旦相同就会报错
2. 使用functools.wraps装饰器修改闭包函数的函数信息
解决方法:
@functools.wraps(f) # 此处为Python内置装饰器(非flask装饰器)可以让被装饰的函数使用指定函数的函数信息(函数名__name__, 函数文档__doc__):
接口分析
1. 渲染方式: 局部刷新–> 前端渲染 -->定义新路由(news/news_collect),实现新闻收藏(收藏与否通过两个标签来实现)
2. 数据库行为: 使用外键(关系属性)来关联用户和收藏的新闻
接口文档
新闻收藏/取消收藏–>/news/news_collect
请求方式 —>POST/json
请求参数 news_id —>新闻id// action 行为 collect/cancel_collect
响应形式 —> json
demo
# 新闻收藏
@news_blu.route('/news_collect', methods=['POST'])
@user_login_data
def news_collect():
user = g.user # 获取登录的用户信息
if not user: # 用户未登录
return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])
# 获取参数
news_id = request.json.get("news_id")
action = request.json.get("action")
# 校验参数
if not all([news_id, action]):
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])
if action not in ["collect", "cancel_collect"]:
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])
try:
news_id = int(news_id)
except BaseException as e:
current_app.logger.error(e)
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])
# 查询新闻模型
try:
news = News.query.get(news_id)
except BaseException as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])
# 数据库行为: 使用关系属性 来关联用户和收藏的新闻
if action == "collect": # 收藏
user.collection_news.append(news)
else: # 取消收藏
user.collection_news.remove(news)
# json返回结果
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK])
tips: 采用g变量来进行传递数据
模板渲染---->data-newid="{{news.id}}":用于构建新闻收藏时的请求参数
渲染方式: 全局刷新—>后端渲染–>在详情路由中,渲染页面
数据库行为:查询新闻是否被当前用户收藏
demo:
is_collected = False # 记录是否收藏了该新闻
if user: # 判断用户是否登录
# 查询新闻是否被当前用户收藏
if news in user.collection_news:
is_collected = True
user = user.to_dict() if user else None
# 将新闻数据传入模板渲染
return render_template("detail.html", news=news.to_dict(), user=user, rank_list=rank_list, is_collected=is_collected)
tips:利用is_collected = False
来传递是否收藏信息,后边也会用到类似变量的传递渲染所需的模板变量(is_collected=is_collected)
模板渲染
收藏
tips
:
基本实现思想和收藏/显示收藏一致,需要区分局部刷新(2)和页面刷新(3)
demo:
收藏
已收藏取消收藏
{% if user %}
{% else %}
登录发表你的评论
{% endif %}
显示评论数量
评论数量在新闻的详情页展示,故可以直接在news/detail里面做渲染:
{{ news.comments_count }}
—>中间有to_dict的转换字典格式
显示当前新闻的所有评论可在model的评论里添加条目:
comments = db.relationship("Comment", lazy="dynamic")
自关联多对一关系属性
父评论id
# 自关联多对一关系属性, 需要设置remote_side=[主键名]
# parent = db.relationship("Comment", remote_side=[id])
# children = db.relationship("Comment") # 一对多
# parent = db.relationship('Comment', remote_side=[id]) # 多对一
children = db.relationship("Comment", backref=db.backref('parent', remote_side=[id]))
接口分析
渲染方式:局部刷新—>前端渲染---->定义路由,实现评论
数据库行为:增加一条评论数据
接口文档
新闻评论/子评论
/news/news_comment
请求方式
POST
请求参数
comment 评论内容
news_id 新闻id
parent_id 父评论id ---->子评论需要参数展示
响应形式
json
tips
:需要注意的是子评论与父评论的一对多情况是需要跟据页面详情展示所决定的,此处为一子评论对多父评论.
代码实现
# 新闻评论
@news_blu.route('/news_comment', methods=['POST'])
@user_login_data
def news_comment():
user = g.user
if not user: # 用户未登录
return jsonify(errno=RET.SESSIONERR, errmsg=error_map[RET.SESSIONERR])
# 获取参数
comment_content = request.json.get("comment")
news_id = request.json.get("news_id")
parent_id = request.json.get("parent_id")
# 校验参数
if not all([comment_content, news_id]):
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])
try:
news_id = int(news_id)
except BaseException as e:
current_app.logger.error(e)
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])
# 数据库行为: 增加一条评论数据
comment = Comment()
comment.content = comment_content
comment.news_id = news_id
comment.user_id = user.id
if parent_id: # 传parent_id, 说明是子评论
try:
parent_id = int(parent_id)
comment.parent_id = parent_id
except BaseException as e:
current_app.logger.error(e)
return jsonify(errno=RET.PARAMERR, errmsg=error_map[RET.PARAMERR])
try:
db.session.add(comment)
db.session.commit() # 为了在视图函数中生成评论id, 必须手动提交(自动提交在视图函数执行完才会生成评论id)
except BaseException as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR, errmsg=error_map[RET.DBERR])
# json返回数据 需要将评论的id也返回(用于前端发子评论时, 构建请求参数parent_id)
return jsonify(errno=RET.OK, errmsg=error_map[RET.OK], data=comment.to_dict())
tips
:
1. db.session.commit() # 为了在视图函数中生成评论id, 必须手动提交(自动提交在视图函数执行完才会生成评论id)-------->data=comment.to_dict()
2. 因篇幅有限,数据库的操作未做rool_back处理
接口分析
渲染方式:全局刷新---->后端渲染---->详情路由中,模板渲染
数据库行为:查询该新闻的所有评论
代码实现
# 查询该新闻的所有评论 按照发布时间倒序排列
try:
comments = news.comments.order_by(Comment.create_time.desc()).all()
except BaseException as e:
current_app.logger.error(e)
return abort(500)
user = user.to_dict() if user else None
# 将新闻数据传入模板渲染
return render_template("detail.html", news=news.to_dict(), user=user, rank_list=rank_list, is_collected=is_collected, comments=[comment.to_dict() for comment in comments])
tips:comments=[comment.to_dict() for comment in comments]
---->列表推导式,区分三元运算符
# 定义自关联多对一关系属性时, 必须设置形参remote_side=[主键]
# children = db.relationship("Comment") # 一对多
# parent = db.relationship('Comment', remote_side=[id]) # 多对一
children = db.relationship("Comment", backref=db.backref('parent', remote_side=[id]))