一、功能需求分析

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,,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": "",
    "errmsg": "",
    "data": {
    "news_id": ,
    "content_id": ,
    "content": "评论比较中肯。",
    "author": "admin",
    "update_time": "2019年08月19日 16:00",
    "parent": {
    "news_id": ,
    "content_id": ,
    "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异常\n{}'.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('服务器超时,请重试!');
}) }) });

Django项目: 6.新闻详情页的更多相关文章

  1. [ionic开源项目教程] - 第10讲 新闻详情页的用户体验优化

    目录 [ionic开源项目教程] 第1讲 前言,技术储备,环境搭建,常用命令 [ionic开源项目教程] 第2讲 新建项目,架构页面,配置app.js和controllers.js [ionic开源项 ...

  2. [ionic开源项目教程] - 第9讲 新闻详情页的实现

    目录 [ionic开源项目教程] 第1讲 前言,技术储备,环境搭建,常用命令 [ionic开源项目教程] 第2讲 新建项目,架构页面,配置app.js和controllers.js [ionic开源项 ...

  3. react-native 项目实战 -- 新闻客户端(7) -- 新闻详情页

    http://c.3g.163.com/nc/article/BUH64L0J00031H2L/full.html 观察这个地址,BUH64L0J00031H2L 就是每条新闻数据里的postid.  ...

  4. phpcms新闻详情页上一篇下一篇的实现

    在新闻详情页(show.html或show_*.html) 只需要添加类似如下代码即可: <div>上一篇:<a href="{$previous_page[url]}&q ...

  5. Python爬虫:新浪新闻详情页的数据抓取(函数版)

    上一篇文章<Python爬虫:抓取新浪新闻数据>详细解说了如何抓取新浪新闻详情页的相关数据,但代码的构建不利于后续扩展,每次抓取新的详情页时都需要重新写一遍,因此,我们需要将其整理成函数, ...

  6. Django项目: 5.新闻主页

    一.功能需求分析 1.功能 轮播图 推荐文章列表 文章标签导航 文章列表 分页 二.模型设计 根据功能分析,我们需要如下表 1.表和字段分析 文章分类表 文章表 文章评论表 推荐文章表 轮播图表 2. ...

  7. node——由新闻列表跳转到新闻详情页

    当我们在浏览新闻列表页面的时候,想要看感兴趣的新闻内容,需要到详情页面去查看内容. 在之前写好了新闻列表页面,现在需要做列表页面到详情页面的跳转,需要考虑一下问题 1.点击新闻列表某一项跳转到详情页面 ...

  8. 潭州课堂25班:Ph201805201 django 项目 第二十三课 文章主页 轮播图前端实现 热门新闻推荐实现 详情页实现 (课堂笔记)

    前台代码 // 在static/js/news/index.js文件中 $(function () { // 新闻列表功能 let $newsLi = $(".news-nav ul li& ...

  9. android124 zhihuibeijing 新闻中心-新闻 -北京页签 下拉刷新

    缓存工具类:以url为key,json数据为value, package com.itheima.zhbj52.utils; import com.itheima.zhbj52.global.Glob ...

随机推荐

  1. http://edu.manew.com/ ,蛮牛教育(很少免费),主要是unty3D和大数据方向。适合扫盲

    http://edu.manew.com/ ,蛮牛教育(很少免费),主要是unty3D和大数据方向.

  2. The Python Standard Library

    The Python Standard Library¶ While The Python Language Reference describes the exact syntax and sema ...

  3. PAT甲级——A1131 Subway Map【30】

    In the big cities, the subway systems always look so complex to the visitors. To give you some sense ...

  4. vue 返回上一页

    参考:https://www.cnblogs.com/chenguiya/p/9118265.html 注:需为history模式 方法一: @click="back" back( ...

  5. java 测试时 程序的 运行时间

    检测一个JAVA程序的运行时间方法:long startTime = System.currentTimeMillis();//获取当前时间//doSomeThing();   //要运行的java程 ...

  6. CDH断电后 hbase出现spilt块不完整问题

    从错误看起来是regionspilt时候断电了,导致hbase master启动不起来,因为是测试环境只能删除这些region了,掉一部分数据 删除hbase下spilt块,删除zK里面的habse ...

  7. ES6和常用特性归纳

    ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了.Mozilla公司将在这个标准的基础上,推出JavaScript 2.0. ECMAS ...

  8. Leetcode166. Fraction to Recurring Decimal分数到小数

    给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以字符串形式返回小数. 如果小数部分为循环小数,则将循环的部分括在括号内. 示例 1: 输入: numerator ...

  9. 小程序怎样控制rich-text中的<img>标签自适应

    小程序通过rich-text实现字符串转化为html,即 <rich-text node="{{html}}"></rich-text>.如果html字符串 ...

  10. Ionic3 demo TallyBook 实例2

    1.添加插件 2.相关页面 消费页面: <ion-header> <ion-navbar> <ion-title> 消费记录 </ion-title> ...