zoukankan      html  css  js  c++  java
  • Django项目: 6.新闻详情页

    一、功能需求分析

    1.功能

    1. 新闻详情

    2. 加载评论功能

    3. 添加评论功能

    二、新闻详情页

    1.业务流程分析

    业务流程:

    1. 判断前端传递新闻id是否为空,是否为整数,是否存在

    2.接口设计

    1. 接口说明:

    类目说明
    请求方法 GET
    url定义 /news/<int:news_id>/
    参数格式 url路径参数
    1. 参数说明:

    参数名类型是否必须描述
    news_id 整数 新闻id
    1. 返回结果:

      html页面,直接通过模板渲染的方式实现

    3.后端代码

    视图

    # 在news/views.py中定义如下视图
    class NewDetailView(View):
        def get(self, request, news_id):
            news = News.objects.select_related('tag', 'author').only(
                'title', 'content', 'update_time', 'tag__name', 'author__username').filter(
                is_delete=False, id=news_id).first()
            if news:
                return render(request, 'news/news_detail.html', context={
                    'news': news,
                })
            else:
                return HttpResponseNotFound('<h1>Page not found</h1>')
            # 快捷方式
            # 1. 去数据库获取新闻数据
            # news_queryset = News.objects.select_related('tag', 'author').only('title', 'content', 'update_time', 'tag__name', 'author__username')
            # news = get_object_or_404(news_queryset, is_delete=False, id=news_id)
    
            # 2. 返回渲染页面
            # return render(request, 'news/news_detail.html', context={'news': news})

    路由

    # 在news/urls.py中定义如下路由
    
    urlpatterns = [
    #....
        path('news/<int:news_id>/', views.NewDetailView.as_view(), name='news_detail')
    ]

    4.前端代码

    html

    <!-- templates/news/news_detail.html -->
    {% extends 'base/base.html' %}
    {% load static %}
    {% block title %}文章详情{% endblock %}
    {% block link %}
     <link rel="stylesheet" href="{% static 'css/news/news-detail.css' %}">
    {% endblock %}
    
    {% block main_contain %}
            <!-- news-contain start  -->
        <div class="news-contain">
          <h1 class="news-title">{{ news.title }}</h1>
          <div class="news-info">
            <div class="news-info-left">
              <span class="news-author">{{ news.author.username }}</span>
              <span class="news-pub-time">{{ news.update_time }}</span>
              <span class="news-type">{{ news.tag.name }}</span>
            </div>
          </div>
          <article class="news-content">
            {{ news.content|safe }}
          </article>
          <div class="comment-contain">
            <div class="comment-pub clearfix">
              <div class="new-comment">
                文章评论(<span class="comment-count">0</span>)
              </div>
              <div class="comment-control please-login-comment" style="display:none;">
                <input type="text" placeholder="请登录后参加评论">
              </div>
              <div class="comment-control logged-comment">
                <input type="text" placeholder="请填写评论">
              </div>
              <button class="comment-btn">发表评论</button>
            </div>
            <ul class="comment-list">
              <li class="comment-item">
                <div class="comment-info clearfix">
                  <img src="../images/avatar.jpeg" alt="avatar" class="comment-avatar">
                  <span class="comment-user">评论人</span>
                  <span class="comment-pub-time">1小时前</span>
                </div>
                <div class="comment-content">这是一条评论</div>
              </li>
              <li class="comment-item">
                <div class="comment-info clearfix">
                  <img src="../images/avatar.jpeg" alt="avatar" class="comment-avatar">
                  <span class="comment-user">评论人</span>
                  <span class="comment-pub-time">1小时前</span>
                </div>
                <div class="comment-content">这是一条评论</div>
              </li>
            </ul>
          </div>
    
        </div>
        <!-- news-contain end  -->
    {% endblock %}
    {% block script %}
    {% endblock %}

    css

    /* 为文章内容添加样式 */
    /* 在static/css/news/news-detail.css文件中需要添加如下内容:*/
    
    .news-content p {
        font-size: 16px;
        line-height: 26px;
        text-align: justify;
        word-wrap: break-word;
        padding: 3px 0
    }

    三、加载新闻评论

    1.接口设计

    新闻详情页,直接渲染新闻评论

    2.后端代码

    模型代码

    # 本项目设计二级评论,修改Comments模型,添加一个parent字段
    parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True)

    修改模型后一定要及时迁移

    导入数据,文件见我的文件中的sql数据包.rar

    # 导入测试数据tb_comments_20181222.sql
    # 一定要保证tb_users中有id为1,2,3的三个用户,不然导入测试数据会报错
    mysql -u用户名 -p -D 数据库名< tb_comments_20181222.sql

    注意: 如果你想删除有外键关联的表是不能在navicat中用查询-新建查询的命令truncate table_name 来删除表中所有数据的,只能重新弄过

    视图代码

    # 修改news/views.py中的NewsDetailView视图
    class NewDetailView(View):
        def get(self, request, news_id):
            news = News.objects.select_related('tag', 'author').only(
                'title', 'content', 'update_time', 'tag__name', 'author__username').filter(
                is_delete=False, id=news_id).first()
            if news:
                comments = Comments.objects.select_related('author', 'parent').only(
                    'content', 'author__username', 'update_time', 'parent__author__username', 'parent__content',
                    'parent__update_time').filter(is_delete=False, news_id=news_id)
                return render(request, 'news/news_detail.html', context={
                    'news': news,
                    'comments': comments
                })
            else:
                return HttpResponseNotFound('<h1>Page not found</h1>')

    3.前端代码

    css

    /* 在static/css/news/news-detail.css中添加如下代码: */
    .comment-list .comment-item {
      /*把这条样式注释掉*/
      /*border-bottom: 1px solid #ddd;*/
      margin-bottom: 30px;
    }
    /* ========= 为父评论添加样式 start============ */
    .left_float{
        float:left;
    }
    
    .right_float{
        float:right;
    }
    
    .parent_comment_text{
        width:698px;
        padding:8px;
        background: #f4facf;
        margin:10px 0 0 60px;
    }
    
    .comment_time{
        font-size:12px;
        color:#999;
        margin:10px 0 0 60px;
    }
    
    .parent_comment_text .parent_username{
        font-size:12px;
        color:#000;
        display:inline-block;
    }
    .parent_comment_text .comment_time{
       display: inline-block;
       float:right;
    }
    
    .parent_comment_text .parent_content_text{
        color:#666;
        font-size:14px;
        margin-top: 20px;
    }
    
    .reply_a_tag{
        font-size:12px;
        color:#999;
        text-indent:20px;
        margin:10px 0 0 20px;
        background:url('/static/images/content_icon.png') left center no-repeat;
    }
    
    .reply_form{
        width:718px;
        overflow:hidden;
        margin:10px 0 0 60px;
        display:none;
    }
    
    .reply_input{
        float:left;
        width:692px;
        height:30px;
        border-radius:4px;
        padding:10px;
        outline:none;
        border:1px solid #2185ed;
    }
    
    .reply_btn,.reply_cancel{
        width:40px;
        height:23px;
        background:#76b6f4;
        border:0px;
        border-radius:2px;
        color:#fff;
        margin:10px 5px 0 10px;
        cursor:pointer;
    }
    
    .reply_cancel{
        background:#fff;
        color: #909090;
    }
    /* ========= 为父评论添加样式 end============ */

    将content_icon.png图片放到static/images/中

    html

    <!-- 在templates/news/news_detail.html文件中class="comment-contain"里面加入如下代码: -->
    <div class="comment-contain">
                <div class="comment-pub clearfix">
                    <div class="new-comment">
                        文章评论(<span class="comment-count">0</span>)
                    </div>
                    <div class="comment-control please-login-comment" style="display:none;">
                        <input type="text" placeholder="请登录后参加评论">
                    </div>
                    <div class="comment-control logged-comment">
                        <input type="text" placeholder="请填写评论">
                    </div>
                    <button class="comment-btn">发表评论</button>
                </div>
                <ul class="comment-list">
                    {% for comment in comments %}
                        <li class="comment-item">
                            <div class="comment-info clearfix">
                                <img src="/static/images/avatar.jpeg" alt="avatar" class="comment-avatar">
                                <span class="comment-user">{{ comment.author.username }}</span>
                                <span class="comment-pub-time">{{ comment.update_time }}</span>
                            </div>
                            <div class="comment-content">{{ comment.content }}</div>
    
                            {% if comment.parent %}
                                <div class="parent_comment_text">
                                    <div class="parent_username">{{ comment.parent.author }}</div>
                                    <div class="comment_time">{{ comment.parent.update_time }}</div>
    
                                    <div class="parent_content_text">
                                        {{ comment.parent.content }}
                                    </div>
    
                                </div>
                            {% endif %}
    
                            <a href="javascript:void(0);" class="reply_a_tag right_float">回复</a>
                            <form class="reply_form left_float" comment-id="{{ comment.id }}"
                                  news-id="{{ comment.news_id }}">
                                <textarea class="reply_input"></textarea>
                                <input type="button" value="回复" class="reply_btn right_float">
                                <input type="reset" name="" value="取消" class="reply_cancel right_float">
                            </form>
    
                        </li>
                    {% endfor comments %}
    
                </ul>
            </div>

    js代码

    // 在static/js/news/news_detail.js中加入如下代码:
    
    $(function () {
      $('.comment-list').delegate('a,input', 'click', function () {
        //获取回复按钮的class属性
        let sClassValue = $(this).prop('class');
        // 如果点击的是回复按钮,就显示输入框
        if (sClassValue.indexOf('reply_a_tag') >= 0) {
          $(this).next().toggle();
        }
        // 如果点击的是取消按钮,就隐藏输入框
        if (sClassValue.indexOf('reply_cancel') >= 0) {
          $(this).parent().toggle();
        }
    
        if (sClassValue.indexOf('reply_btn') >= 0) {
          // 评论
        }
      });
     
    });

    四、添加新闻评论功能

    1.业务流程分析

    业务处理流程:

    1. 判断用户是否登录

    2. 判断前端传的新闻id是否为空,是否我整数,是否存在

    3. 判断评论内容是否为空

    4. 判断是否有父评论,父评论id是否与新闻id匹配

    5. 保存新闻评论

    2.接口设计

    1. 接口说明:

    类目说明
    请求方法 POST
    url定义 /news/<int:news_id>/comment/
    参数格式 url路径参数,表单参数
    1. 参数说明:

    参数名类型是否必须描述
    news_id 整数 新闻id
    content 字符串 新闻评论内容
    parent_id 整数 父评论id

    注意:post请求需要携带csrftoken

    1. 返回结果:

      {
          "errno": "0",
          "errmsg": "",
          "data": {
              "news_id": 1170,
              "content_id": 3569,
              "content": "评论比较中肯。",
              "author": "admin",
              "update_time": "2019年08月19日 16:00",
              "parent": {
                  "news_id": 1170,
                  "content_id": 893,
                  "content": "行文思路简单肤浅,文章结构平面呆板。",
                  "author": "xinlan",
                  "update_time": "2018年12月21日 11:17",
                  "parent": null
              }
          }
      }

    3.后端代码

    视图代码

    # 在news/views.py中编写如下视图
    class NewsCommentView(View):
        """
        添加评论视图
        url: /news/<int:news_id>/comment/
        """
        def post(self, request, news_id):
            # 是否登录
            if not request.user.is_authenticated:
                return json_response(errno=Code.SESSIONERR, errmsg=error_map[Code.SESSIONERR])
            # 新闻是否存在
            if not News.objects.only('id').filter(is_delete=False, id=news_id).exists():
                return json_response(errno=Code.PARAMERR, errmsg='新闻不存在!')
    
            content = request.POST.get('content')
            # 内容是否为空
            if not content:
                return json_response(errno=Code.PARAMERR, errmsg='评论内容不能为空!')
    
            # 父id是否正常
            parent_id = request.POST.get('parent_id')
            if parent_id:
                try:
                    parent_id = int(parent_id)
                    if not Comments.objects.only('id').filter(is_delete=False, id=parent_id, news_id=news_id).exists():
                        return json_response(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])
                except Exception as e:
                    logger.info('前端传递过来的parent_id异常
    {}'.format(e))
                    return json_response(errno=Code.PARAMERR, errmsg='未知异常')
    
            # 保存到数据库
            new_comment = Comments()
            new_comment.content = content
            new_comment.news_id = news_id
            new_comment.author = request.user
            new_comment.parent_id = parent_id if parent_id else None
            new_comment.save()
    
            return json_response(data=new_comment.to_dict_data())

    序列化comment对象(也就是在上面代码的结尾处用到的方法to_dict_data的书写), 这里我们只实现了2层评论关系,无法多个嵌套

    # 在news/models.py的Comment模型中添加如下方法,用来序列化
    def to_dict_data(self):
        comment_dict = {
            'news_id': self.news_id,
            'content_id': self.id,
            'content': self.content,
            'author': self.author.username,
            'update_time': self.update_time.astimezone().strftime('%Y年%m月%d日 %H:%M'),
            'parent': self.parent.to_dict_data() if self.parent else None
        }
        return comment_dict

    路由

    # 在news/urls.py中添加如下路由
    path('news/<int:news_id>/comment/', views.NewsCommentView.as_view(), name='news_comment'),

    4.前端代码

    html

    <!-- 修改templates/news/news_detail.html中评论部分代码如下 -->
    <!-- news comment start -->
            <div class="comment-contain">
                <div class="comment-pub clearfix">
                    <div class="new-comment">
                        文章评论(<span class="comment-count">0</span>)
                    </div>
                    {% if user.is_authenticated %}
    
                    <div class="comment-control logged-comment" news-id="{{ news.id }}">
                        <input type="text" placeholder="请填写评论">
                    </div>
                    {% else %}
                    <div class="comment-control please-login-comment">
                        <input type="text" placeholder="请登录后参加评论" readonly>
                    </div>
                    {% endif %}
                    <button class="comment-btn">发表评论</button>
                    {% csrf_token %}
                </div>
               
            <!-- news comment end -->

    js代码

    // 修改static/js/news/news_detail.js中的代码如下
    // 修改static/js/news/news_detail.js中的代码如下
    $(function () {
        // 对评论进行评论
        $('.comment-list').delegate('a,input', 'click', function () {
            //获取回复按钮的class属性
            let sClassValue = $(this).prop('class');
            // 如果点击的是回复按钮,就显示输入框
            if (sClassValue.indexOf('reply_a_tag') >= 0) {
                $(this).next().toggle();
            }
            // 如果点击的是取消按钮,就隐藏输入框
            if (sClassValue.indexOf('reply_cancel') >= 0) {
                $(this).parent().toggle();
            }
    
            if (sClassValue.indexOf('reply_btn') >= 0) {
                // 评论
                let $this = $(this);
                let news_id = $this.parent().attr('news-id');
                let parent_id = $this.parent().attr('comment-id');
                let content = $this.prev().val();
                if (!content) {
                    message.showError('请输入评论内容!');
                    return
                }
                $
                    .ajax({
                        url: '/news/' + news_id + '/comment/',
                        type: 'POST',
                        data: {
                            content: content,
                            parent_id: parent_id
                        },
                        dataType: "json"
                    })
    
                    .done((res) => {
                        if (res.errno === '0') {
                            let comment = res.data;
                            let html_comment = `<li class="comment-item">
                <div class="comment-info clearfix">
                  <img src="/static/images/avatar.jpeg" alt="avatar" class="comment-avatar">
                  <span class="comment-user">${comment.author}</span>
                </div>
                <div class="comment-content">${comment.content}</div>
    
                    <div class="parent_comment_text">
                      <div class="parent_username">${comment.parent.author}</div>
                      <div class="comment_time">${comment.parent.update_time}</div>
                      <div class="parent_content_text">
                        ${comment.parent.content}
                      </div>
                    </div>
    
                  <div class="comment_time left_float">${comment.update_time}</div>
                  <a href="javascript:;" class="reply_a_tag right_float">回复</a>
                  <form class="reply_form left_float" comment-id="${comment.content_id}" news-id="${comment.news_id}">
                    <textarea class="reply_input"></textarea>
                    <input type="button" value="回复" class="reply_btn right_float">
                    <input type="reset" name="" value="取消" class="reply_cancel right_float">
                  </form>
    
              </li>`;
                            message.showSuccess('评论成功!');
                            setTimeout(() => {
                                $('.comment-list').prepend(html_comment);
                            }, 800);
    
                            $this.prev().val('');   // 清空输入框
                            $this.parent().hide();  // 关闭评论框
                        } else if (res.errno === '4101') {
                            // 用户未登录
                            message.showError(res.errmsg);
                            setTimeout(() => {
                                window.location.href = '/user/login/'
                            }, 800)
                        } else {
                            // 失败
                            message.showError(res.errmsg)
                        }
                    })
                    .fail(() => {
                        message.showError('服务器超时,请重试')
                    })
            }
        });
        // 对新闻评论
        let $newsComment = $('.logged-comment input');            // 新闻评论框
        let $sendComment = $('.comment-pub .comment-btn');           // 新闻评论按钮
    
        $sendComment.click(function () {
    
            let $this = $(this);
            if ($this.prev().hasClass('please-login-comment')) {
                message.showError('未登录,请登录后再评论!');
                setTimeout(() => {
                    window.location.href = '/user/login/'
                }, 800);
                return
            }
            let news_id = $this.prev().attr('news-id');
            let content = $newsComment.val();
            if (!content) {
                message.showError('请输入评论内容!');
                return
            }
            $
                .ajax({
                    url: '/news/' + news_id + '/comment/',
                    type: 'POST',
                    data: {
                        content: content
                    },
                    dataType: 'json'
                })
                .done((res) => {
                    if (res.errno === '0') {
                        let comment = res.data;
                        let html_comment = `<li class="comment-item">
                <div class="comment-info clearfix">
                  <img src="/static/images/avatar.jpeg" alt="avatar" class="comment-avatar">
                  <span class="comment-user">${comment.author}</span>
                  <span class="comment-pub-time">${ comment.update_time }</span>
                </div>
                <div class="comment-content">${comment.content}</div>
    
                  <a href="javascript:;" class="reply_a_tag right_float">回复</a>
                  <form class="reply_form left_float" comment-id="${comment.content_id}" news-id="${comment.news_id}">
                    <textarea class="reply_input"></textarea>
                    <input type="button" value="回复" class="reply_btn right_float">
                    <input type="reset" name="" value="取消" class="reply_cancel right_float">
                  </form>
    
              </li>`;
                        message.showSuccess('评论成功!');
                        setTimeout(() => {
                            $(".comment-list").prepend(html_comment);
                        }, 800);
    
                        // 清空
                        $newsComment.val('');
    
                    } else if (res.errno === '4101') {
                        // 用户未登录
                        message.showError(res.errmsg);
                        setTimeout(() => {
                            window.location.href = '/user/login/'
                        }, 800)
    
                    } else {
                        message.showError(res.errmsg);
                    }
                })
    
                .fail(() => {
                    message.showError('服务器超时,请重试!');
                })
    
        })
    
    
    });
  • 相关阅读:
    js学习(4) 函数
    Js学习(3) 数组
    NGUI的UILabel
    unity模型部分替换
    工作流程
    unity 资源内存管理
    unity 跑酷demo
    unity动画相关
    unity之C#回调函数
    maya导入unity
  • 原文地址:https://www.cnblogs.com/Tmclri/p/11552702.html
Copyright © 2011-2022 走看看