DAY 13用户新闻收藏功能实现和页面渲染
收藏按钮的渲染
在我们新闻详情页中我们需要对收藏按钮进行不同的渲染
第一种是收藏

第二种是已收藏:

我们先来进行简单的分析,这是关于新闻详情页的渲染,所以我们需要来到我们
渲染详情页的视图函数中进行代码的编写,收藏功能是针对登录用户的操作,未登录用户
是不能进行的。所以我们需要先判断用户的登录状态,没有登录就不能进行第二种已收藏,
直接是第一种的渲染,还有就是对登录用户的信息进行判断,查看是否收藏过这条新闻,如果收藏
过我们就展示第二种,如果没有收藏就展示第一种。
而这里渲染第一种的情况有两种,没有登录或者登录后没有收藏,所以我们可以先将展示第一种的
判断条件设定为FLASE,只有满足了登录且收藏后才是TRUE。也就是第二种渲染
大致理清思路后我们就开始代码的编写
先在视图函数中写人注释

然后是代码:
# 4. 收藏相关展示
is_collected = False #设定默认为FALSE
if user: #用户已登录
if news in user.collection_news : #用户已收藏
is_collected = True
# 生成保存用户变量的上下文并传到前台进行相关判断处理
context = {
"user": user,
"news_click_list": news_click_list,
"news":news.to_dict(),
"is_collected":is_collected #生成上下文给前端
}
前端的代码为:
<a href="javascript:;" class="collected block-center" data-newid="{{ news.id }}" style="display: {% if is_collected %} block
{% else %} none {% endif %};"><span class="out">已收藏</span><span class="over">取消收藏</span></a>
<a href="javascript:;" class="collection block-center" data-newid="{{ news.id }}" style="display: {% if is_collected %} none
{% else %} block {% endif %};">收藏</a>
校验:我们可以通过往用户和新闻创建的第三张收藏表里面加入测试数据,也就是当前的用户ID和新闻ID手动加到
数据库中,然后刷新页面查看按钮是否变成已收藏来完成
用户收藏功能的实现以及取消收藏
当我们完成了收藏按钮的渲染,我们还需要完成当登录用户点击
按钮时完成新闻的收藏,用户和新闻是多对对的关系,所以我们需要操作建立多对多关系的第三张表,
也就是我们的用户新闻收藏表中点击收藏时添加我们的数据,以及点击取消收藏时对相应数据的删除
这里是一个新的接口,所以我们需要建立一个符合接口规范的新的视图函数:

视图函数创建在我们的news模块文件夹里,因为是新闻页的内容,写在渲染详情页视图的后面
安装接口设计图来写URL和函数名,请求方式为post,记得设置

完成之后还是写文档注释,实现的功能:

然后在先整理思路后写上文字 步骤,简单分析:
首先这个功能是用户登录后才有的,所以在接收参数之前判断用户是否登录,
如果没有,我们就不需要进行后续的步骤,直接返回错误码和错误信息就行,让前台再根据错误码进行
相应的操作,比如弹出提示登录信息或者直接跳转到登录界面
这里补充一下用户校验过滤器的问题:
关于用户校验我们使用了过滤器,但是我们在之前的渲染页面也使用了过滤器
这就导致了我们视图的__name__变成了统一的值,也就是过滤器返回的wrapper。
视图函数是不允许这种情况的发生的,所以我们需要将__name__的值变回原来的视图函数所生成的值
需要在我们的过滤器中加入一个过滤器来完成:

warps 完成的就是把__name__改回原来的值,设置后就没问题了。
完成校验用户的登录状态之后,接下来才是接收数据(new_id,action),action对应了不同的参数,分别为取消收藏和
收藏,也就是不同的操作
接收完成后进行校验,先判断参数是否齐全,然后校验接收到的news_id 的数据类型,然后再判断传入的action是否合法,是否为指定的
两个值的其中一个,最后才是进行数据库查询,我们需要将查询操作放在最后进行,这样省时。如果先进行数据库查询,查询后其他的数据错误的话又要再次进行查询后再
次进行后面的操作,放在后面当前面发送错误就不需要操作数据库了。查询news_id对应的新闻是否存在
完成校验之后就是对不同action的值进行不同的操作,最后返回结果就完成了,完成分析后对代码进行编写

第一步
# 1.判断用户是否登录
user = g.user
if not user :
return jsonify(errno=RET.SESSIONERR,errmsg="用户未登录")
这里使用过滤器后直接通过g变量获取就行,没有返回None ,然后再进行判断返回结果
第二步
# 2. 接收参数(news_id, action)
news_id = request.json.get("news_id")
action = request.json.get("action")
这里接收的是json数据,进行相应的查询代码查询就行(request.json.get),没有的包记得导入。
第三步
# 3.校验参数
# 3.1判断参数是否传齐
if not all([news_id,action]):
return jsonify(errno=RET.PARAMERR,errmsg="参数缺失")
# 3.2判断news_id是否为整形
try:
news_id = int(news_id)
except Exception as e :
current_app.logger.error(e)
return jsonify(errno=RET.PARAMERR,errmsg="参数错误")
# 3.3判断action是否合法
if action not in ["collect","cancel_collect"]:
return jsonify(errno=RET.PARAMERR,errmsg="非法参数")
# 3.4判断新闻是否存在
try:
news = News.query.filter_by(id = news_id).first()
except Exception as e :
current_app.logger.error(e)
return jsonify(errno=RET.DBERR,errmsg="数据库读取失败")
if not news : #对新闻是否存在进行处理
return jsonify(errno=RET.NODATA,errmsg="新闻未找到")
这里通过强转来判断整形,当强转失败也就是传入了不符合的数据类型并作出处理。action的判断创建了一个内表,通过判断
传入的action的值是否在内表里来进行,这样拓展性更强,而且比起 == 的判断代码量少。
通过news_id来进行查询并对结果进行判断,不要忘记对结果进行判断处理这一步
第四步
# 4.通过action来进行收藏操作
if action == "collect" :
if news not in user.collection_news: #判断新闻对象是否在列表里
user.collection_news.append(news) #往新闻对象的列表里加入新的新闻对象
else:
if news in user.collection_news:
user.collection_news.remove(news)
try:
db.session.commit() #提交到数据库
except Exception as e :
current_app.logger.error(e)
db.session.rollback() #修改数据,事物,回滚
return jsonify(errno=RET.DBERR,errmsg="操作数据库失败")
action接收到的值只有两个,所以这里可以用双分支来进行处理,通过判断里再判断对逻辑步骤进行说明,比起合并这样更能
展现每一步的实现的思路和用意。
首先通过双分支来对不同的值进行处理,collect表示的是收藏该新闻,也就是在我们对象属性所参生的新闻对象列表里加入查询到的
新闻对象,用append来进行列表对象的添加。首先还是要先判断该对象是否在列表里,没有再进行添加。
user.collection_news所产生的数据为有新闻对象的列表,我们查询的news也是一个新闻对象,这里是对多对多功能的使用,对应模型User类的获取数据的
字段,这个字段只是方便我们操作第三张表来获取数据,并不是存在的字段

然后就是例外的值,也就是取消收藏,从类表中把对应的新闻对象移除(remove),也需要先判断列表中新闻是否存在。
完成记得提交到数据库,而且对数据进行了操作,要设置回滚,操作失败把数据还原到之前。
第五步
# 5.返回数据
return jsonify(errno=RET.OK,errmsg="成功")
返回成功的码给前台进行判断执行js 文件,完成后台的修改之后对前端js文件进行修改
前端代码
找到详情页detailHTML的detail.js文件,然后进行代码的修改,也就是覆盖粘贴
// 收藏
$(".collection").click(function () {
// 参数:要收藏哪条新闻
var params = {
'news_id':$(this).attr('data-newid'),
'action':'collect'
};
$.ajax({
url: '/news/news_collect',
type: 'post',
data: JSON.stringify(params),
contentType: 'application/json',
headers: {'X-CSRFToken':getCookie('csrf_token')},
success:function (response) {
if (response.errno == "0") {
// 收藏成功,隐藏收藏按钮
$(".collection").hide();
// 显示取消收藏按钮
$(".collected").show();
}else if (response.errno == "4101"){
$('.login_form_con').show();
}else{
alert(resp.errmsg);
}
}
});
});
// 取消收藏
$(".collected").click(function () {
// 参数:要取消收藏哪条新闻
var params = {
'news_id':$(this).attr('data-newid'),
'action':'cancel_collect'
};
$.ajax({
url: '/news/news_collect',
type: 'post',
data: JSON.stringify(params),
contentType: 'application/json',
headers: {'X-CSRFToken':getCookie('csrf_token')},
success:function (response) {
if (response.errno == "0") {
// 取消收藏成功,隐藏收藏按钮
$(".collection").show();
// 显示取消收藏按钮
$(".collected").hide();
}else if (response.errno == "4101"){
$('.login_form_con').show();
}else{
alert(resp.errmsg);
}
}
});
});
粘贴之后就完成了,修改了js文件浏览器记得清缓存
对端代码进行简单分析:
首先需要在用户进行了收藏和取消收藏后进行页面的刷新,但是不需要全部刷新,所以使用了js中的ajax来实现
局部刷新,用ajax的回调函数来执行完成后的处理。

浙公网安备 33010602011771号