f9.4 新闻展示

文章目录

  • 一.新闻首页
    • 1.排行列表(重点)
    • 2.获取新闻列表(重点)
    • 3优化
      • 1.过滤 分类/最新
      • 2.新闻分类列表
  • 二.新闻详情
    • 1.显示详情页面(重点)
    • 2.抽取基类
    • 3.显示其他数据
  • 三.收藏新闻
    • 1.收藏/取消收藏(重点)
    • 2.显示收藏情况(重点)
  • 四.评论/回复
    • 1.显示评论框
    • 2.评论/回复基本实现(重点)
    • 3.显示评论情况(重点)

远程仓库地址
https://gitee.com/cz_zzz/InfoNews24

一.新闻首页

1.排行列表(重点)

  • 接口分析
    渲染方式: 全局刷新/SEO -->后端渲染–>跟路由
    数据库行为:查询点击量排行前10的新闻
  • 代码实现
    在首页路由中, 查询点击量排行前10的新闻
    将排行数据传入模板渲染
index.html:

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")
    
    

    2.获取新闻列表(重点)

    • 接口分析
      渲染方式:局部刷新–>前端渲染–>定义路由,返回数据
      数据库行为:按照分类和页码来查询(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:

    1. 排序时先按时间排好后再分页
    2. jsonify返回前先进行格式转换:
      "news_list": [news.to_dict() for news in pn.items],
      tips: 存在问题—>最新一项没有展示
    • 分类”最新”
    # 数据库行为:  按照分类和页码查询新闻  按发布时间倒序
        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])
    

    3优化

    1.过滤 分类/最新

    # 数据库行为:  按照分类和页码查询新闻  按发布时间倒序
    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:注释部分为原来思路,本次考虑用实参前面加*–>类似于解包---->如果没有值,则结果是""(空字符串)

    2.新闻分类列表

    • 接口分析:
      渲染方式: 全局刷新–>后端渲染–> 跟路由
      数据库行为: 查询分类数据

    • 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 }}"用于构建获取新闻列表的请求参数

    f9.4 新闻展示_第1张图片

    • 装饰器方式封装数据查询---->抽取函数和采用装饰器均可
      --------对于相同的展示页面可以提取为函数
      f9.4 新闻展示_第2张图片

    • 装饰器bug
      1. flask中不允许同一个蓝图定义的路由的函数标记相同,一旦相同就会报错
      2. 使用functools.wraps装饰器修改闭包函数的函数信息
      解决方法:

        	@functools.wraps(f)  # 此处为Python内置装饰器(非flask装饰器)可以让被装饰的函数使用指定函数的函数信息(函数名__name__, 函数文档__doc__):
      
    1. url_map指向示例f9.4 新闻展示_第3张图片
      浏览器提示重复指向:
      f9.4 新闻展示_第4张图片
    2. flask指向修改f9.4 新闻展示_第5张图片
    3. 多个装饰器引发的指向问题f9.4 新闻展示_第6张图片
    4. @functools.wraps(f)修改f9.4 新闻展示_第7张图片
    5. 修改结果

    f9.4 新闻展示_第8张图片

    三.收藏新闻

    1.收藏/取消收藏(重点)

    2.显示收藏情况(重点)

    • 接口分析
      1. 渲染方式: 全局刷新—>后端渲染–>在详情路由中,渲染页面

      2. 数据库行为:查询新闻是否被当前用户收藏
        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:

    1. TODO注释---->方便前后端的交流
    2. 上边的两个标签是互斥行为,仅在同一位置显示一个,欲知详情可以查看css文件

    四.评论/回复

    基本实现思想和收藏/显示收藏一致,需要区分局部刷新(2)和页面刷新(3)

    1.显示评论框

    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]))
      

    2.评论/回复基本实现(重点)

    • 接口分析
      渲染方式:局部刷新—>前端渲染---->定义路由,实现评论
      数据库行为:增加一条评论数据

    • 接口文档
      新闻评论/子评论
      /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处理
    

    3.显示评论情况(重点)

    • 接口分析
      渲染方式:全局刷新---->后端渲染---->详情路由中,模板渲染
      数据库行为:查询该新闻的所有评论

    • 代码实现

        # 查询该新闻的所有评论  按照发布时间倒序排列
        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]---->列表推导式,区分三元运算符

    • 模板渲染
      tips:
    1. 区分{{ comment.user.nick_name }}和{{ comment.parent.user.nick_name }}
    # 定义自关联多对一关系属性时, 必须设置形参remote_side=[主键]
    # children = db.relationship("Comment")  # 一对多
    # parent = db.relationship('Comment', remote_side=[id])  # 多对一
    
    children = db.relationship("Comment", backref=db.backref('parent', remote_side=[id]))
    

    你可能感兴趣的:(flask,flask)