6.1 django项目-新闻博客系统之新闻详情页

06 新闻详情页

一、功能需求分析

1、功能

  • 新闻详情
  • 评论加载
  • 评论添加

二、新闻详情页

1、接口设计

  1. 接口说明

    项目项目 说明说明
    请求方法 GET
    url /news/<int:news_id>/
    参数类型 查询参数
  2. 返回结果

    新闻详情html

2、后端代码

def news_detial(request, news_id):
    if News.objects.filter(id=news_id, is_delete=False).exists():
        return render(request, 'news/news_detail.html',context={'news_id': news_id})
    else:
        return HttpResponseNotFound('<h1>Page not found</h1>')

3、前端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 %}
      <div class="news-contain" news_id="{{ news_id }}">
      <h1 class="news-title"></h1>
      <div class="news-info">
        <div class="news-info-left">
          <span class="news-author">作者</span>
          <span class="news-pub-time">1小时前</span>
          <span class="news-type">类型</span>
        </div>
      </div>
      <article class="news-content">
        文章内容
      </article>
      <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">
                    <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>
        <ul class="comment-list">

        </ul>
      </div>

    </div>
{% endblock %}

{% block otherjs %}
    <script src="{% static 'js/base/message.js' %}"></script>
    <script src="{% static 'js/news/detial.js' %}"></script>
{% endblock otherjs %}

三、新闻详情

1、接口设计

  1. 接口说明

    项目项目 说明说明
    请求方法 GET
    url /news/detiallist/
    参数类型 查询参数
  2. 参数说明

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

    {
        "error": "0",
        "errmsg": "",
        "data": {
            'news_id': news_id,
            'news':{
                'title': title,
                'content': content,
                'update_time': update_time,
                'tag_name': tag_name,
                'author_username': author_username
                }
        }
    }
    

2、后端代码

def news_detial_list(request):
    try:
        news_id = int(request.GET.get('news_id', 1))
    except Exception as e:
        logger.error('新闻id错误:\n{}'.format(e))
        news_id = 1

    news = News.objects.values('title', 'content', 'update_time').annotate(
        tag_name=F('tag__name'),
        author_username=F('author__username')).filter(id=news_id, is_delete=False)

    news_list = list(news)

    if news_list:
        data = {
            'news_id': news_id,
            'news': news_list
        }

        return json_response(data=data)
    else:
        return json_response(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])

路由

path('news/detiallist/', views.news_detial_list, name='detial_list'),

四、评论

1、模型设计

class Comments(BaseModel):
    """
    评论模型
    """
    content = models.TextField('内容', help_text='内容')
    author = models.ForeignKey('user.User', on_delete=models.SET_NULL, null=True)
    news = models.ForeignKey('News', on_delete=models.CASCADE)

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

    class Meta:
        ordering = ['-update_time', '-id']  # 排序
        db_table = "tb_comments"  # 指明数据库表名
        verbose_name = "评论"  # 在admin站点中显示的名称
        verbose_name_plural = verbose_name  # 显示的复数名称

    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

2、接口设计

  1. 接口说明

    项目项目 说明说明
    请求方法 GET
    url /news/comments/
    参数类型 查询参数
  2. 参数说明

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

    {
        "error": "0",
        "errmsg": "",
        "data": {
            'news_id': news_id,
            'comments':{
                'id': id,
                'content': content,
                'update_time': update_time,
                'author_username': author_username,
                'parent_author_username': parent_author_username,
                'parent_content': parent_content,
                'parent_update_time': parent_update_time,
                }
        }
    }
    

3、后端代码

def comment_list(request):
    try:
        news_id = int(request.GET.get('news_id', 1))
    except Exception as e:
        logger.error('新闻id错误:\n{}'.format(e))
        news_id = 1

    comments = Comments.objects.values('id',
                'content', 'update_time',).annotate(
        author_username=F('author__username'),
        parent_author_username=F('parent__author__username'),
        parent_content=F('parent__content'),
        parent_update_time=F('parent__update_time'),
    ).filter(news_id=news_id, is_delete=False)

    # print(list(comments))

    data = {
        'news_id': news_id,
        'comments': list(comments)
    }

    return json_response(data=data)

路由

path('news/comments/', views.comment_list, name='comments_list'),

导入数据

五、添加评论

1、接口设计

1、接口说明

项目项目 说明说明
请求方法 POST
url /news/<int:news_id>/addcomments/
参数类型 查询参数

2、参数说明

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

注意:post请求需要携带csrftoken

  1. 返回结果

    {
        "error": "0",
        "errmsg": "",
        "data": {
             'news_id': news_id,
                'content_id': id,
                'content': content,
                'author': author,
                'update_time': update_time
             'parent': {
                    'news_id': news_id,
                    'content_id': id,
                    'content': content,
                    'author': author,
                    'update_time': update_time
                    'parent': null
            }
        }
    }
    
参数名 类型 是否必须 描述
news_id 整数 新闻id
content 字符串 新闻评论内容
parent_id 整数 父评论id

注意:post请求需要携带csrftoken

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

注意:post请求需要携带csrftoken

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

注意:post请求需要携带csrftoken

2、后端代码

def add_comment(request,news_id):
    if request.method == 'POST':
        # 是否登录
        if not request.user.is_authenticated:
            return json_response(errno=Code.SESSIONERR, errmsg=error_map[Code.SESSIONERR])
        form = Checkaddcomments(request.POST)
        if form.is_valid():
            # 校验成功
            # 将评论写入数据库
            # 保存到数据库
            news_id = form.cleaned_data.get('news_id')
            content = form.cleaned_data.get('content')
            parent_id = form.cleaned_data.get('parent_id')
            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())
        else:
            err_msg_str = form.get_errors()
            return json_response(errno=Code.PARAMERR, errmsg=err_msg_str)

路由

path('news/<int:news_id>/addcomments/', views.add_comment, name='addcomments'),

六、前端设计

1、前端js

/*=== newsdetialStart ===*/
$(function () {
    fn_load_newsdetial();
    console.log($('.news-contain').attr('news_id'));
    function fn_load_newsdetial() {
        let $news_id = $('.news-contain').attr('news_id');
        $
            .ajax({
                url:'/news/detiallist/?news_id=' + $news_id,
                type:'GET',
                dataType:'json'
            })
            .done((resp)=>{
                if(resp.errno === '0')
                {
                    let $news = resp.data.news[0];
                    $('.news-title').html($news.title);
                    $('.news-author').html($news.author_username);
                    $('.news-pub-time').html($news.update_time);
                    $('.news-type').html($news.tag_name);
                    $('.news-content').html($news.content);

                }
                else
                {
                    message.showError(resp.errmsg)
                }
            })
            .fail(()=>{
                message.showError('服务器超时,请重试!')
            })
    }
});
/*=== newsdetialEnd ===*/

/*=== commentsStart ===*/
$(function () {
    fn_load_comments();
    function fn_load_comments() {
        let $news_id = $('.news-contain').attr('news_id');
        $
            .ajax({
                url:'/news/comments/?news_id=' + $news_id,
                type:'GET',
                dataType:'json'
            })
            .done((resp)=>{
                if(resp.errno === '0')
                {
                    // message.showSuccess('评论获取成功');
                    resp.data.comments.forEach(function (comment) {
                        let $parent=``;
                        if(comment.parent_author_username)
                        {
                            $parent = `<div class="parent_comment_text">
                                <div class="parent_username">${comment.parent_author_username}</div>
                                <div class="comment_time">${comment.parent_update_time}</div>

                                <div class="parent_content_text">
                                    ${comment.parent_content}
                                </div>

                            </div>`;
                        }
                    let content = `<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>`
                        + $parent +
                        `<a href="javascript:void(0);" class="reply_a_tag right_float">回复</a>
                        <form class="reply_form left_float" comment-id="${comment.id}"
                              news-id="${resp.data.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>`;
                    $('.comment-list').append(content);
                    })

                }
                else
                {
                    message.showError(resp.errmsg)
                }
            })
            .fail(()=>{
                message.showError('服务器超时,请重试!')
            })
    }
});



// 修改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 parent_id = $this.parent().attr('comment-id');
            let content = $this.prev().val();
            let news_id = $('.news-contain').attr('news_id');
            if (!content) {
                message.showError('请输入评论内容!');
                return
            }
            $
                .ajax({
                    url: '/news/' + news_id + '/addcomments/',
                    type: 'POST',
                    data: {
                        news_id:news_id,
                        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 = '/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 = '/login/'
            }, 800);
            return
        }
        let news_id = $('.news-contain').attr('news_id');
        let content = $newsComment.val();
        if (!content) {
            message.showError('请输入评论内容!');
            return
        }
        $
            .ajax({
                url: '/news/' + news_id + '/addcomments/',
                type: 'POST',
                data: {
                    news_id:news_id,
                    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('服务器超时,请重试!');
            })

    })


});
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容