首页 > 其他分享 >BBS-文章详情页面

BBS-文章详情页面

时间:2023-05-15 21:12:14浏览次数:40  
标签:点赞点 up 评论 详情 文章 article BBS id 页面

目录
点击文章标题跳转到具体的文章中去

重点功能:

  • 点赞点踩数前端实时显示
  • 后端要对is_up做反序列化
  • 评论临时渲染
  • 对于子评论,要获取parent_id的值

一、添加路由

urls.py

1、详情页路由

    # 文章详情页路由:/站点名/article/文章的id
    url(r'^(?P<username>\w+)/article/(?P<article_id>\d+)$', views.article_detail),

这个路由放到位置在这三个中是最下的

2、点赞点踩路由

    # 点赞点踩路由
    url(r'^up_and_down/$', views.up_and_down),

3、评论功能路由

    # 评论功能路由
    url(r'^comment/$', views.comment),

二、文章详情功能前端

文章详情页前端步骤

1. 继承base.html页面
2. 模板变量显示文章标题和正文内容
3. 点赞点踩样式。复制博客园的样式。
4. 点赞点踩绑定事件
5. 评论功能
6. 评论按钮绑定点击事件
7. 评论列表
8. 回复功能绑定事件
9. 第6步中加多行子评论中的parent_id参数

点赞点踩js步骤

1. html标签搭建好。复制博客园的样。
2. 复制html,css样式(所有标签的样式都要复制),对有图片防盗链的图片,复制到我们的项目中,更改图片路径。
3. 数据用文章表中点赞数点踩数展示
4. 点赞点踩js,绑定点击事件。给点赞点踩的div定义相同的类,给这个类绑定事件
    4.1 区分开到底点的是赞还是踩,判断某个元素有没有这个类:hasClass()方法,返回布尔值
    4.2 获取数据,获取is_up字段数据,文章id数据
    4.3 发起ajax请求,把数据提交到django 的后端
    4.4 接收后端的数据
    4.5 点赞成功显示的文字信息
    4.6 不成功显示文字信息
    4.7 点赞数实时展示,用js。逻辑:要先拿到原来的数量,然后+1,再写进去

博客园点赞点踩绑定事件思路分析:

<div id="div_digg">
    <div class="diggit" onlick="votePost(17391134, 'Digg')"><!--绑定事件的另一种写法-->
        <span class="diggnum" id="digg_count">{{ article_detail.up_num }}</span><!--点赞数-->
    </div>
    <div class="buryit" onlick="votePost(17391134, 'Bury')">
        <span class="burynum" id="bury_count">{{ article_detail.down_num }}</span><!--点踩数-->
    </div>

function votePost(id, s) {
    if (s == 'Digg') {
    	// 点赞逻辑
    } else {
    	// 点踩逻辑
    }
    }

评论功能

前端逻辑:

# 评论功能
样式,发表评论文字,一个大段文本框,一个按钮  # 可以使用富文本编辑器


# 评论按钮绑定点击事件
1. 获取参数,直接去comment表中查看有哪些字段必须从前端获取
2. 发起ajax请求,把数据提交到后端
3. 临时渲染评论的内容,需要评论用户和文本内容,参数中增加一个获取用户(sessionu获取)
    3.1 es6中的模板语法,反引号
    3.2 把html追加到指定元素之后,$(A).append(B)
    3.3 清空输入框里面的评论内容
4. 加参数数据:父id。根评论,parent_id为空。子评论,parent_id有值
    # 在事件之前定义全局变量,parent_id
    

# 评论列表
1. 复制一个列表标签
2. 数据展示
    几楼,用forloop.counter计数,从1开始
    评论时间,格式化
    当前评论的用户,评论表找用户表,正向
    回复a链接,href="javascript:;",增加两个属性,当前评论用户名,当前评论用户id。回复事件中使用
    评论内容
    

# 回复功能绑定事件
1. 点击回复后文本框中显示@用户名加换行符,获取焦点事件
2. 给回复标签加两个属性当前用户当前id,js中直接获取标签属性
3. parent_id的值获取,是回复的评论id

前端代码

文章详情页面,在templates文件夹中新建article_detial.html,继承的基础模板base.html

<!-- 文章详情页面 -->
{% extends 'base.html' %}

{% block css %}
    {# 点赞点踩css样式开始 #}
    <style>
        #div_digg {
            float: right;
            margin-bottom: 10px;
            margin-right: 30px;
            font-size: 12px;
            width: 125px;
            text-align: center;
            margin-top: 10px;
        }

        .diggit {
            float: left;
            width: 46px;
            height: 52px;
            background: url('/static/img/upup.png') no-repeat; /*更改成自己静态文件的路径*/
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .buryit {
            float: right;
            margin-left: 20px;
            width: 46px;
            height: 52px;
            background: url('/static/img/downdown.png') no-repeat; /*更改成自己静态文件的路径*/
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .clear {
            clear: both;
        }

        .diggword {
            margin-top: 5px;
            margin-left: 0;
            font-size: 12px;
            color: #808080;
        }
    </style>
    {# 点赞点踩css样式结束 #}
{% endblock %}

{% block content %}
    {# 文章内容展示开始 #}
    <h1 class="article_title">{{ article_obj.title }}</h1><!-- 文章标题 -->
    <div class="article_content"><!-- 文章内容 -->
        {{ article_obj.content|safe }}<!-- safe能显示标签样式 -->
    </div>
    {# 文章内容展示结束 #}

    {# 点赞点踩样式开始 #}
    <div class="clearfix"><!-- 解决父标签塌陷,加个类clearfix -->
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">{{ article_obj.up_num }}</span><!--点赞数,从文章表中查询出来的-->
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">{{ article_obj.down_num }}</span><!--点踩数,从文章表中查询出来的-->
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color: red"></div><!--一个空div,用来添加点赞之后成功或者失败显示的文本-->
        </div>
    </div>
    {# 点赞点踩样式结束 #}

    {# 评论列表展示开始 #}
    <div>
        <p><span>评论列表</span></p>
        <ul class="list-group">
            {% for comment in comment_list %}
                <li class="list-group-item">
                    {# #1楼 2023-05-11 17:32 哈哈哈哼#}
                    <span># {{ forloop.counter }}楼 </span><!-- 几楼,用forloop计数,从1开始 -->
                    <span>{{ comment.comment_time|date:'Y-m-d H:i' }}</span><!-- 评论时间,格式化 -->
                    <!-- 通过当前评论找用户,正向 -->
                    <span>{{ comment.user.username }}</span><!-- 评论的用户 -->
                    <span class="pull-right"><!-- 回复链接 -->
                        <!-- href为空,点击就会刷新页面,解决方法1:"#",解决方法2:href="javascript:;"-->
                        <!-- javascript:,冒号后面可以写js代码,执行了a标签后就会直接执行冒号后的js代码 -->
                            <a href="javascript:;" comment_username="{{ comment.user.username }}"
                               comment_id="{{ comment.pk }}" class="reply">回复</a>
                        </span>
                    <p>
                        {{ comment.content }}
                    </p><!-- 评论内容 -->
                </li>
            {% endfor %}
        </ul>
    </div>
    {# 评论列表展示结束 #}

    {# 评论功能开始  #}
    <div>
        <p><span class="glyphicon glyphicon-comment">发表评论</span></p>
        <p>
            <textarea name="" id="content" cols="30" rows="10"></textarea><!-- 大段文本框 -->
        </p>
        <button class="btn btn-success id_comment">提交评论</button>
    </div>
    {# 评论功能结束  #}

    <div style="height: 500px"></div>
{% endblock %}


{% block js %}
    <script>
        {# 点赞点踩js开始 #}
        $(".action").click(function () {  // 给点赞点踩的div定义相同的类,给这个类绑定事件
            var _this = $(this);

            // 1.区分开到底点的是赞还是踩,判断某个元素有没有这个类:hasClass()方法,返回布尔值
            // $(this).hasClass('diggit');  // 如果返回的是true,说明点的是赞,否则是踩

            // 2.获取数据
            // 2.1 获取is_up字段的数据
            var is_up = $(this).hasClass('diggit');
            // console.log(is_up, typeof is_up)  // true 'boolean'

            // 2.2 获取文章id,文章详情对象点pk
            var article_id = '{{ article_obj.pk }}'

            // 2.3 用户id,后端从可以session里获取

            // 3.发起ajax请求,把数据提交到django 的后端
            $.ajax({
                url: '/up_and_down/',  // 点赞点踩路由
                type: 'post',
                data: {is_up: is_up, article_id: article_id, csrfmiddlewaretoken: '{{ csrf_token }}'},
                success: function (res) {
                    // 4.接收后端的数据
                    if (res.code == 200) {
                        // 5. 点赞成功显示的文字信息
                        $("#digg_tips").text(res.msg);  // 文本操作

                        // 7.点赞数实时展示,用js。逻辑:要先拿到原来的数量,然后+1,再写进去
                        // 7.1 在外面先用一个变量把当前的action的jquery对象保存下来:_this
                        {# 这里用this指的是function函数 #}

                        // 7.2 先拿到原来的值
                        {# _this:表示外面的$(".action")标签,这样点赞点踩都能拿到;.children():是拿到儿子元素的div,就是具体的点赞或点踩的div;.text() #}
                        var old_num = _this.children().text();
                        // console.log(old_num, typeof old_num);  // 3 string

                        // 7.3 值加一后添加到原来的位置
                        // 字符串需要转格式才能相加减
                        _this.children().text(parseInt(old_num) + 1);
                    } else {
                        // 6. 不成功,给div加个文本
                        // 识别标签的用html(value)设置值
                        $("#digg_tips").html(res.msg);
                    }
                }
            })
        })
        {# 点赞点踩js结束 #}

        {# 评论按钮绑定点击事件开始 #}
        // 根评论和子评论都是使用这个按钮
        // 定义全局变量,直接在两个事件之前就定义parent_id
        var parent_id = null;

        $(".id_comment").click(function () {
            // 1. 获取参数,直接去comment表中查看有哪些字段必须从前端获取
            // 1.1 评论内容
            var content = $("#content").val();

            // 1.2 获取文章id
            var article_id = '{{ article_obj.pk }}';

            // 用户从哪里来?
            var current_user = '{{ request.session.username }}'

            // 2. 发起ajax请求,把数据提交到后端
            $.ajax({
                url: '/comment/',  // 评论路由
                type: 'post',
                // 4. 加参数数据:父id。根评论,parent_id为空。子评论,parent_id有值
                data: {content: content, article_id: article_id, csrfmiddlewaretoken: '{{ csrf_token }}', parent_id:parent_id},
                success: function () {
                    // 3.临时渲染
                    var html = '';
                    // 3.1 es6中的模板语法,反引号,多行文本, ${变量名占位}
                    html = `
                    <li class="list-group-item">
                        <span>${current_user}</span>
                        <p>
                            ${content}
                        </p>
                    </li>
                    `  // 变量有用户名和评论内容,需要提前获取到

                    // 3.2 把html追加到指定元素之后, $(A).append(B),把B追加到A标签之后
                    $('.list-group').append(html);

                    // 3.3 清空输入框里面的评论内容
                    $('#content').val('');
                }
            });
        });
        {# 评论按钮绑定点击事件结束 #}

        {# 回复功能绑定事件开始 #}
        // 回复功能
        $(".reply").click(function () {
            // 2. 给回复标签加两个属性当前用户名、当前id,js中直接获取标签属性
            // 获取回复标签中的用户名属性的值
            var comment_username = $(this).attr('comment_username')

            // 3. parent_id的值获取,是回复的评论id
            // var parent_id = $(this).attr('comment_id');  这样写parent_id是局部变量,想要按钮点击事件中也能使用,就把parent_id设置为全局变量
            parent_id = $(this).attr('comment_id');

            // 子评论需要哪些参数?
            // 评论的内容
            // 需要评论id

            // 1. 点击回复后文本框中显示@用户名加换行符,获取焦点事件
            $("#content").val('@' + comment_username + '/n').focus();
        })
        {# 回复功能绑定事件结束 #}
    </script>

{% endblock %}

三、添加视图函数

文章详情页

步骤:

1. 首页、站点页中文章标题处添加路由
    href="/{{ username }}/article/{{ article.pk }}"
2. 验证用户是否存在,因为只要跳转,网址是可以更改的
3. 获取网址中的username,查询用户信息,用户不存在返回404页面
4. 根据文章id查询文章对象
5. 读取当前文章的所有评论,按照当前文章id来过滤
``

代码:
```python
@login_auth
def article_detail(request, username, article_id):
    """文章详情页"""
    # 1. 路由
    # 2. 根据用户名查询用户信息
    user_obj = models.UserInfo.objects.filter(username=username).first()
    # 3. 验证用户是否存在,因为只要跳转,网址是可以更改的
    if not user_obj:
        return render(request, '404.html')

    # 4. 根据文章id查询文章对象
    article_obj = models.Article.objects.filter(pk=article_id).first()

    # 5. 读取当前文章的所有评论,按照当前文章id来过滤
    comment_list = models.Comment.objects.filter(article_id=article_id).all()

    return render(request, 'article_detail.html', locals())

点赞点踩

后端步骤:

1. 定义返回给前端的数据格式
2. 接收参数
3. 验证参数
    3.1 用户必须登录
    3.2 自己的文章自己不能点赞,先通过当前文章去查询用户(谁写的),然后与当前登录用户比较
    3.3 验证一篇文章一个人只能点一次。根据文章id和用户id两个条件,去表里面查询,如果查到了,说明已经点过了,不能再点了
4. 正常业务逻辑
    4.1 对is_up做反序列化:json.loads
    4.2 操作文章表,更新数据。点赞点踩要操作不同的字段,需要先判断
    4.3 操作点赞点踩表,增加记录
5. 返回给前端数据

"""问题:"""
为什么ajax提交到后端的数据被序列化了,查看编码方式,查看前端打印的is_up的type类型

代码:

from django.db.models import F  # 数据变动加一
import json  # 序列化模块


@login_auth
# 单独开设一个方法,来处理点赞点踩逻辑
def up_and_down(request):
    """点赞点踩函数,不需要页面"""
    """
    点赞点踩重要功能分析:
        1. 必须登录
        2. 如果没有登录,在页面上显示:请先登录,并且可以点击,跳转到登录页面
        3. 自己的文章不能给自己点
        4. 如果登录成功了,前端页面上点赞数据要+1,显示点赞成功
        5. 如果点赞过,就不能再点了,也就是一篇文章一个用户只能点一次
    """
    if request.method == 'POST':
        # 1. 定义返回给前端的数据格式
        back_dic = {'code': 200, 'msg': '点赞成功'}

        # 2. 接收参数
        is_up = request.POST.get('is_up')  # true <class 'str'>
        print(is_up, type(is_up))  # 为什么格式是序列化后的格式
        article_id = request.POST.get('article_id')

        # 3.验证参数
        # 3.1 用户必须登录
        if not request.session.get('username'):
            back_dic['code'] = 1010
            back_dic['msg'] = '<a href="/login/" style="color:red">请先登录</a>'  # 后端直接返回a标签
            return JsonResponse(back_dic)

        # 3.2 自己的文章自己不能点
        # 先通过当前文章去查询用户(谁写的),然后与当前登录用户比较
        # 文章找作者:文章--> 站点 --> 用户表
        # 先查询出当前的文章对象
        article_obj = models.Article.objects.filter(pk=article_id).first()
        # article_obj.blog.userinfo.username ----> request.session.get(username)
        if article_obj.blog.userinfo.username == request.session.get('username'):
            back_dic['code'] = 1011
            back_dic['msg'] = '不能给自己点赞或点踩'
            return JsonResponse(back_dic)

        # 3.3 验证一篇文章一个人只能点一次
        # 根据文章id和用户id两个条件,去表里面查询,如果查到了,说明已经点过了,不能再点了
        is_click = models.UpAndDown.objects.filter(article_id=article_id, user_id=request.session.get('id')).first()
        if is_click:
            back_dic['code'] = 1012
            back_dic['msg'] = '你已经点过了,不能再点了!!'
            return JsonResponse(back_dic)

        # 4.处理正常的业务逻辑
        # 入库,操作哪些表?  1. 点赞点踩表 2. 文章表:点赞数,点踩数
        # 4.1 这里需要对is_up做反序列化:json.loads
        is_up = json.loads(is_up)
        # 4.2 操作文章表,更新数据
        if is_up:  # 说明是点赞了
            models.Article.objects.filter(pk=article_id).update(up_num=F('up_num') + 1)  # 增加数据,用到F查询
        else:  # 说明是点踩了
            models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') + 1)
            back_dic['msg'] = '我会更加努力的'  # 点踩需要改一下消息

        # 4.3 操作点赞点踩表,增加记录
        models.UpAndDown.objects.create(is_up=is_up, article_id=article_id, user_id=request.session.get('id'))
        # models.UpAndDown.objects.create(is_up=is_up, article(不加id)=article_obj(文章对象))

        return JsonResponse(back_dic)

评论功能

后端步骤:

1. 定义返回前端的数据格式
2. 接收参数
3. 验证参数,内容不能为空
4. 处理正常业务逻辑,操作2张表,1 文章表(评论数),2 评论表
    4.1 更新文章表中的评论数量
    4.2 新增评论表中的一条记录
    4.3 练习一下事务的使用
5.返回给前端数据

后端代码:

@login_auth
def comment(request):
    """
    评论函数,不需要页面
    """
    """
    评论功能分析:
        1. 必须登录之后才能评论,前端的评论框必须是登录之后才显示
        2. 先做根评论
        3. 在做子评论
    """
    if request.method == 'POST':
        # 1. 定义返回前端的数据格式
        back_dic = {'code': 200, 'msg': '评论成功'}

        # 2. 接收参数
        content = request.POST.get('content')
        article_id = request.POST.get("article_id")
        parent_id = request.POST.get("parent_id")

        # 3. 验证参数
        # 内容不能为空
        if not content:
            back_dic['code'] = 1012
            back_dic['msg'] = '评论内容不能为空'
            return JsonResponse(back_dic)

        # 4.处理正常业务逻辑
        # 操作2张表,1 文章表(评论数) 2 评论表

        # 4.3 练习一下事务的使用
        from django.db import transaction
        try:
            with transaction.atomic():
                # 4.1 更新文章表中的评论数
                models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1)

                # 4.2 新增评论表中的一条记录
                models.Comment.objects.create(content=content, article_id=article_id, user_id=request.session.get('id'),
                                              parent_id=parent_id)
                # user_id=request.session.get('id'),企业中这些经常使用的会直接封装成函数,我们直接调用函数获得值
        except Exception as e:
            print(e)
            transaction.rollback()

        # 5.返回给前端数据
        return JsonResponse(back_dic)

复习知识点:
1.es6中的模板语法

可以定义大段文本,并且实现格式化字符操作

<!-- es6中的模板语法使用 -->
var name = 'kevin';  
var age = 18;

console.log(`my name is ${name}, my age is ${age}`);

<!-- 输出结果是:my name is kevin, my age is 18 -->

2.forloop的用法:https://www.cnblogs.com/zjyao/p/17360259.html#for标签

总结

怎么防止路由冲突呢?

1.路由越精准的越往前放,
2.写完一个函数跟路由,函数中先print或显示个页面,先测试一下,看路由是否能够正常访问

排查错误

1.点击,network没有反应,是前端的问题
2.前端,查看console
3.network飘红,是后端的问题
4.后端,点击变红的请求,Header请求跟响应,payload,前端传给后端的参数,前面都没有问题,preview中会有报错信息。
5.请求没有到后端,可能是路由的问题,路由冲突

标签:点赞点,up,评论,详情,文章,article,BBS,id,页面
From: https://www.cnblogs.com/zjyao/p/17400416.html

相关文章

  • Citect2018R2使用报警页面功能做操作记录1
    这一篇学习笔记我在新浪博客记录过,地址是Citect2018R2使用报警页面功能做操作记录1_来自金沙江的小鱼_新浪博客(sina.com.cn)这两天练习了做报警页面,稍微扩展一下,可以做操作记录功能。使用unityv13.1新建一个项目,简单配置一下硬件,新建变量: 新建程序段   这个练......
  • citect2018R2报警函数练习1-做一个简单的报警显示页面
    这一个笔记我在新浪博客记录过,地址是Citect2018R2报警函数练习1-做一个简单的报警显示页面_来自金沙江的小鱼_新浪博客(sina.com.cn) 这两天看citect一些文档,想着练习一下Cicode的报警函数。新建一个Unity项目,简单的配一下硬件 写简单的程序新建一个Citect2018R2程序,使......
  • Citect2018R2报警页面练习1续:显示出报警状态
    这一篇学习笔记我在新浪博客记录过,地址是Citect2018R2报警页面练习1续:显示出报警状态_来自金沙江的小鱼_新浪博客(sina.com.cn)昨天练习了作业个报警信息页面,显示的报警无法区分是到来的还是离去的,有没有确认,虽然颜色上不一样,但操作人员显然不会去记忆每种颜色什么含义,需要有文......
  • Citect2018R2报警函数练习2:报警页面过滤报警
    这一片学习笔记我在新浪博客记录过,地址是Citect2018R2报警函数练习2:报警页面过滤报警_来自金沙江的小鱼_新浪博客(sina.com.cn)昨天练习了在页面上通过cicode控件和函数来做一个报警页面,包括翻页和报警确认。昨天对32个报警做了分类,分成4类和5类。如果希望报警页面只是显示4类报......
  • iframe里面的页面调用父窗口,左右窗口js函数的方法
     iframe里面的页面调用父窗口,左右窗口js函数的方法实现iframe内部页面直接调用该iframe所属父窗口自定义函数的方法。比如有A窗口,A内有个IFRAMEB,B里面的装载的是C页面,这时C要直接调用A里面的一个自定义函数AFUN();那么只要在C页面中写如下JS函数就可以了:window.parent.AFUN()......
  • fullcalendar日程events接收到数据但是并没有渲染到前端页面上(已解决)
    前端确认是能够接收到后端的数据而且跟前端预设的数据一模一样但是就是无法显示到前端上我使用的fullcalendar版本是6.1.7,events使用数组形式[],fullcalendar文档是英文的,例子比较少就没发现这种方法,解决方法调用fullcalendarAPI进行事件移除let calendarApi = this.$refs.......
  • BBS-博客首页
    目录一、添加路由1、首页路由2、修改密码路由3、退出登录路由4、个人头像路由首页首页前端首页前端步骤首页前端代码首页后端后端代码前端重点前端数据展示逻辑文章旁的图片展示修改密码修改密码前端步骤修改密码后端后端逻辑认证登录的装饰器后端代码退出登录后端逻辑后端代码后......
  • BBS-登录功能
    目录一、添加路由1、登陆路由2、生成随机验证码路由二、登录功能前端模板登录前端步骤登录前端代码重点:点击图片自动更新三、添加视图函数后端步骤后端代码1、添加视图函数login2、随机验证码重点功能:随机验证码点击图片换一张验证码后端要先判断验证码的是否正确一、添加......
  • BBS-注册功能
    目录一、添加路由二、注册功能前端模板前端步骤:前端代码前端重点操作注册功能图片实时展示注册功能ajax提交数据三、添加视图函数后端步骤后端代码重点功能:上传图片头像图片实时展示ajax方式提交给后端数据一、添加路由在路由urls.py中要先导入视图层和一些用到的模块fr......
  • edge浏览器网页长截图插件(将页面宽度设窄也适用)
    该插件的缺点:有时会排版错误;对于有些页面,会缺少部分文字;不能从“只当前位置向下”截图,只能整个页面或可见区域。插件安装和使用科X上网;用edge访问:捕捉网页截图-FireShot的-MicrosoftEdgeAddons打开要截图的页面点击该扩展:或等待自动滚动完成:另存为图片或“复......