BBS03--文章详情页、点赞点踩、文章评论

1 文章详情页

# url设计
  /username/article/1

# 先验证url是否会被其他url顶替

# 文章详情页和个人站点基本一致 所以用模版继承

# 侧边栏的渲染需要传输数据才能渲染 并且该侧边栏在很多页面都需要使用
  1.哪个地方用就拷贝需要的代码(不推荐 有点繁琐)
  2.将侧边栏制作成inclusion_tag	


# 自定义模板步骤:
  1.在应用下创建一个名字必须叫templatetags文件夹
  2.在该文件夹内创建一个任意名称的py文件
  3.在该py文件内先固定写两行代码
    from django import template
    register = template.Library()
    # 自定义过滤器
    # 自定义标签
    # 自定义inclusion_tag


# 自定义inclusion_tag:left_menu
from django import template
from app01 import models
from django.db.models import Count
from django.db.models.functions import TruncMonth

register = template.Library()


@register.inclusion_tag('left_menu.html')
def left_menu(username):
    # 构造侧边栏需要的数据
    user_obj = models.UserInfo.objects.filter(username=username).first()
    blog = user_obj.blog

    # 左侧三个筛选 (侧边栏的筛选其实就是对article_list再进一步筛选)
    # 1.查询个人站点的文章分类及分类下的文章数
    category_list = models.Category.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values(
        'name', 'count_num', 'pk')

    # 2.查询个人站点的文章标签及标签下的文章
    tag_list = models.Tag.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values(
        'name', 'count_num', 'pk')

    # 3.查询个人站点下的文章时间及时间下的文章数
    date_list = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values(
        'month').annotate(count_num=Count('id')).values('month', 'count_num').order_by('month')
    return locals()

2 文章点赞点踩

2.1 逻辑梳理

# 引入:
  1.浏览器上你看到的花里胡哨的页面,内部都是HTML(前端)代码
  2.那现在我们的文章内容应该写什么???	>>> html代码
  3.如何拷贝文章
    博客园文章--copy outerhtml

  4.拷贝点赞点踩
    1.拷贝前端点赞点踩图标 只拷了html
    2.css也要拷贝
        由于有图片防盗链的问题 所以将图片直接下载到本地

# 课下思考:
  前端如何区分用户是点了赞还是点了踩
    1.给标签各自绑定一个事件
      两个标签对应的代码其实基本一样,仅仅是是否点赞点踩这一个参数不一样而已
    2.二合一
      给两个标签绑定一个事件
        
    # 给所有的action类绑定事件
    $('.action').click(function () {
        alert($(this).hasClass('diggit'))
    })

# 由于点赞点踩内部有一定的业务逻辑,所以后端单独开设视图函数处理



# 前端页面
  1.拷贝博客园点赞点踩前端样式
    html代码 + css代码
  2.如何判断用户到底点击了哪个图标
    恰巧页面上只有两个图标,所以给两个图标标签添加一个公共的样式类
    然后给这个样式类绑定点击事件
    再利用this指代的就是当前被操作对象 
    利用hasClass判断是否有某个特定的类属性,
    从而判断出到底是两个图标中的哪一个
  3.书写ajax代码朝后端提交数据
  
  4.后端逻辑书写完毕之后 前端针对点赞点踩动作实现需要动态展示提示信息
      1.前端点赞点踩数字自增1 需要注意数据类型的问题
            Number(old_num) + 1
      2.用户没有登陆 需要展示没有登陆提示 并且登陆可以点击跳转
            html()
            |safe
            mark_safe()
# 后端逻辑
  1.先判断用户是否登陆
    request.user.authenticated()
  2.再判断当前文字是否是当前用户自己写的
    通过文章主键值获取文章对象
    之后利用orm查询获取文章对象对应的用户对象与request.user比对
  3.判断当前用户是否已经给当前文章点了
    利用article_obj文章对象和request.user用户对象去点赞点踩表中筛选数据如果有数据则点过没有则没点
  4.操作数据库	需要注意要同时操作两张表
    # 前端发送过来的是否点赞是一个字符串 需要你自己转成布尔值或者用字符串判断
    is_up = json.loads(is_up)
    F模块

2.2 代码实现

##### views.py 

# 个人建议:
   写代码先把所有正确的逻辑写完 再去考虑错误的逻辑 不要试图两者兼得

import json
from django.db.models import F
def up_or_down(request):
    """
    1.校验用户是否登陆
    2.判断当前文章是否是当前用户自己写的(自己不能点自己的文章)
    3.当前用户是否已经给当前文章点过了
    4.操作数据库
    """
    if request.is_ajax():
        back_dic = {'code':1000,'msg':''}
        # 1 先判断当前用户是否登陆
        if request.user.is_authenticated():
            article_id = request.POST.get('article_id')
            is_up = request.POST.get('is_up')
            is_up = json.loads(is_up)  # 记得类型转换
            # print(is_up, type(is_up))  # True <class 'bool'>
            # 2 判断当前文章是否是当前用户自己写的  根据文章id查询文章对象 根据文章对象查作者 根request.user比对
            article_obj = models.Article.objects.filter(pk=article_id).first()
            if not article_obj.blog.userinfo == request.user:
                # 3 校验当前用户是否已经点了      哪个地方记录了用户到底点没点
                is_click = models.UpAndDown.objects.filter(user=request.user,article=article_obj)
                if not is_click:
                    # 4 操作数据库 记录数据      要同步操作普通字段
                    # 判断当前用户点了赞还是踩 从而决定给哪个字段加一
                    if is_up:
                        # 给点赞数加一
                        models.Article.objects.filter(pk=article_id).update(up_num = F('up_num') + 1)
                        back_dic['msg'] = '点赞成功'
                    else:
                        # 给点踩数加一
                        models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') + 1)
                        back_dic['msg'] = '点踩成功'
                    # 操作点赞点踩表
                    models.UpAndDown.objects.create(user=request.user,article=article_obj,is_up=is_up)
                else:
                    back_dic['code'] = 1001
                    back_dic['msg'] = '你已经点过了,不能再点了'  
                    # 这里你可以做的更加的详细 提示用户到底点了赞还是点了踩
            else:
                back_dic['code'] = 1002
                back_dic['msg'] = '你个臭不要脸的!'
        else:
            back_dic['code'] = 1003
            back_dic['msg'] = '请先<a href="/login/">登陆</a>'
        return JsonResponse(back_dic)

    
    
#### article_detail.html    
<script>
    //  给所有的action类绑定事件
    $('.action').click(function () {
        let isUp = $(this).hasClass('diggit');    # digg  n.推荐   bury  n.埋葬
        let $div = $(this);
        // 朝后端发送ajax请求
        $.ajax({
            url:'/up_or_down/',
            type:'post',
            data:{
                'article_id':'{{ article_obj.pk }}',
                'is_up':isUp,
                'csrfmiddlewaretoken':'{{ csrf_token }}'
            },
            success:function (args) {
                    if(args.code == 1000){
                        $('#digg_tips').text(args.msg)
                        // 将前端的数字加一
                        // 先获取到之前的数字
                        let oldNum = $div.children().text();  // 文本 是字符类型
                        // 易错点
                        $div.children().text(Number(oldNum) + 1)  // 字符串拼接了 1+1 = 11  11 + 1 = 111
                    }else{
                        $('#digg_tips').html(args.msg)
                    }
            }
        })
    })
</script>

3 文章评论

# 顺序:
  先根评论
  再子评论

# 根评论
    先把整体的评论功能跑通 再去填补
    
  1.书写前端获取用户评论的标签
    可能点赞点踩有浮动带来的影响
    	clearfix
  2.点击评论按钮发送ajax请求
  
  3.后端针对评论单独开设url处理
    后端逻辑其实非常的简单非常的少
    
  4.针对根评论涉及到前端的两种渲染方式
    1.DOM操作临时渲染评论楼
    	需要用到模版字符串
      	// 临时渲染评论楼
        let userName = '{{ request.user.username }}';
        let temp = `
        <li class="list-group-item">
            <span>${userName}</span>
            <span><a href="#" class="pull-right">回复</a></span>
            <div>
                ${conTent}
            </div>
        </li>
        `
        // 将生成好的标签添加到ul标签内
        $('.list-group').append(temp);
        
    2.页面刷新 永久渲染(render)
    	后端直接获取当前文章对应的所有评论 传递给html页面即可
      前端利用for循环参考博客园评论楼样式渲染评论
      
    3.评论框里面的内容需要清空

   
# 子评论
    从回复按钮入手
    
  点击回复按钮发生了哪些事
    1.评论框自动聚焦	.focus()
    2.评论框里面自动添加对应评论的评论人姓名
        @username\n
    3.评论框内部自动换行
     
        
  # 点击回复按钮之后 我们应该获取到根评论对应的用户名和主键值
    针对主键值 多个函数都需要用 所以用全局变量的形式存储
    
  # 针对子评论内容 需要切割出不是用户写的	"@username\n"
    // 获取用户评论的内容
    let conTent = $('#id_comment').val();
    // 判断当前评论是否是子评论 如果是 需要将我们之前手动渲染的@username去除
    if(parentId){
        // 找到\n对应的索引 然后利用切片 但是前片顾头不顾尾 所以索引+1
       let indexNum = conTent.indexOf('\n') + 1;
       conTent = conTent.slice(indexNum)  // 将indexNum之前的所有数据切除 只保留后面的部分
    }
    
   # 后端parent字段本来就可以为空,所以传不传值 都可以直接存储数据
    
   # 前端针对子评论再渲染评论楼的时候 需要额外的判断
      {% if comment.parent_id %}
           <p>@{{ comment.parent.user.username }}</p>
      {% endif %}
           {{ comment.content }}
        
   # 前端parentId字段每次提交之后需要手动清空 



# 思考:
  1.根评论和子评论点的是同一个按钮: parent_id
    
  2.根评论和子评论的区别
    子评论在其实跟评论的ajax代码中 只需要添加一个父评论id即可
posted @ 2022-07-30 13:13  Edmond辉仔  阅读(113)  评论(0编辑  收藏  举报