点赞点踩及评论功能
文章点赞点踩
点赞点踩样式准备
''' 浏览器上你看到的花里胡哨的页面,内部都是HTML(前端)代码 我们搭建的个人站点里面的文章内容应当写html代码 1.如何拷贝文章 <div class="postBody"> <div id="cnblogs_post_body" class="blogpost-body blogpost-body-html">...</div> == $05 # 将对应文章的这行代码拷贝到admin站点里面 2.如何拷贝点赞点踩 1.拷贝前端点赞点踩图标 只拷了html
<div id="div_digg"></div> 2.css也要拷贝 由于有图片防盗链的问题 所以将图片直接下载到本地 '''
{% extends 'base.html' %} {% block 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/up.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/down.png) no-repeat; text-align: center; cursor: pointer; margin-top: 2px; padding-top: 5px; } .clear { clear: both; } </style> {% endblock %} {% block content %} <h1>{{ article_obj.title }}</h1> <div class="article_content"> {{ article_obj.content|safe }} </div> {# 点赞点踩样式开始#} <div> <div id="div_digg"> <div class="diggit"> <span class="diggnum" id="digg_count">0</span> </div> <div class="buryit"> <span class="burynum" id="bury_count">0</span> </div> <div class="clear"></div> <div class="diggword" id="digg_tips"> </div> </div> </div> {# 点赞点踩样式结束#} {% endblock %}
点赞点踩功能
""" 前端如何区分用户是点了赞还是点了踩 1.给标签各自绑定一个事件 两个标签对应的代码其实基本一样,仅仅是是否点赞点踩这一个参数不一样而已 2.二合一 给两个标签绑定一个事件 // 给所有的action类绑定事件 $('.action').click(function () { alert($(this).hasClass('diggit')) }) 由于点赞点踩内部有一定的业务逻辑,所以后端单独开设视图函数处理 """
from django.contrib import admin from django.urls import path,re_path from app01 import views from django.views.static import serve from BBS import settings urlpatterns = [ path('admin/', admin.site.urls), # 点赞点踩 re_path(r'^up_or_down/', views.up_or_down,name='up_or_down'), ]
{% extends 'base.html' %} {% block 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/up.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/down.png) no-repeat; text-align: center; cursor: pointer; margin-top: 2px; padding-top: 5px; } .clear { clear: both; } </style> {% endblock %} {% block content %} <h1>{{ article_obj.title }}</h1> <div class="article_content"> {{ article_obj.content|safe }} </div> {# 点赞点踩样式开始#} <div> <div id="div_digg"> <div class="diggit action"> <span class="diggnum" id="digg_count">0</span> </div> <div class="buryit action"> <span class="burynum" id="bury_count">0</span> </div> <div class="clear"></div> <div class="diggword" id="digg_tips" style="color: red"> </div> </div> </div> {# 点赞点踩样式结束#} {% endblock %} {% block js %} <script> // 给所有的action属性绑定点击事件 $('.action').click(function(args){ {#alert($(this).hasClass('diggit'))#} let isUp = $(this).hasClass('diggit'); 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> {% endblock %}
import json from django.db.models import F def up_or_down(request): """ 1.校验用户是否登陆 2.判断当前文章是否是当前用户自己写的(自己不能点自己的文章) 3.当前用户是否已经给当前文章点过了 4.操作数据库 :param request: :return: """ 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') # print(is_up,type(is_up)) # true <class 'str'> 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)
文章评论
""" 我们先写根评论,再写子评论 根评论 点击评论按钮需要将评论框里面的内容清空 根评论有两步渲染方式 1.DOM临时渲染 2.页面刷新render渲染 子评论 点击回复按钮发生了几件事 1.评论框自动聚焦 2.将回复按钮所在的那一行评论人的姓名拼接成@username 3.评论框内部自动换行 根评论子评论都是点击同一个按钮朝后端提交数据的 parent_id 根评论子评论区别在哪? parent_id """
根评论
from django.contrib import admin from django.urls import path,re_path from app01 import views from django.views.static import serve from BBS import settings urlpatterns = [ path('admin/', admin.site.urls), # 评论 re_path(r'^comment/',views.comment), ]
# 需要在article_detail函数里面如下代码 # 获取当前文章所有的评论内容 comment_list = models.Comment.objects.filter(article=article_obj) from django.db import transaction def comment(request): # 自己也可以给自己的文章评论内容 if request.is_ajax(): if request.method == 'POST': back_dic = {'code': 1000, 'msg': ''} if request.user.is_authenticated: article_id = request.POST.get('article_id') content = request.POST.get('content') # 直接操作评论表,存储数据(操作两张表) with transaction.atomic(): models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1) models.Comment.objects.create(user=request.user,article_id=article_id,content=content) back_dic['msg'] = '评论成功' else: back_dic['code'] = 1001 back_dic['msg'] = '用户未登陆' return JsonResponse(back_dic)
{% block content %} {# 评论楼渲染开始#} {# #1楼 2023-04-21 17:30 林黎尽致#} <div> <ul class="list-group"> <!--由于样式难看,这里使用了列表组--> {% for comment in comment_list %} <li class="list-group-item"> <span>#{{ forloop.counter }}楼</span> <span>{{ comment.comment_time|date:'Y-m-d h:i:s' }}</span> <span>{{ comment.user.username }}</span> <span><a href="" class="pull-right">回复</a></span> <div> {{ comment.content }} </div> </li> {% endfor %} </ul> </div> {# 评论楼渲染结束#} {# 文章评论样式开始#} {% if request.user.is_authenticated %} <div> <p><span class="glyphicon glyphicon-comment"></span>发表评论</p> <div> <textarea name="comment" id="id_comment" cols="60" rows="10"></textarea> </div> <button class="btn btn-primary" id="id_submit">提交评论</button> <span style="color: red" id="errors"></span> </div> {% else %} <li><a href="{% url 'register' %}">注册</a></li> <li><a href="{% url 'login' %}">登陆</a></li> {% endif %} {# 文章评论样式结束#} {% endblock %}
{% block js %} <script> // 给所有的action属性绑定点击事件 $('.action').click(function (args) { {#alert($(this).hasClass('diggit'))#} let isUp = $(this).hasClass('diggit'); 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) } } }) }) // 用户点击评论按钮朝后端发送ajax请求 $('#id_submit').click(function () { // 获取用户评论的内容 let conTent = $('#id_comment').val(); $.ajax({ url: '/comment/', type: 'post', data: { 'article_id': '{{ article_obj.pk }}', 'content': conTent, 'csrfmiddlewaretoken': '{{ csrf_token }}' }, success: function (args) { if (args.code == 1000) { $('#error').text(args.msg) // 将评论框里面的内容清空 $('#id_comment').val('') // 临时渲染评论楼 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); // 清空全局的parentId parentId = null; } } }) }) </script> {% endblock %}
子评论
优化点
1.
在数据库存储数据时让其去掉@username
// 判断当前评论是否是子评论 如果是 需要将我们之前手动渲染的@username去除 if(parentId){ // 找到\n对应的索引 然后利用切片 但是前片顾头不顾尾 所以索引+1 let indexNum = conTent.indexOf('\n') + 1; conTent = conTent.slice(indexNum) // 将indexNum之前的所有数据切除 只保留后面的部分 }
2.根评论与子评论在前端显示没有区别
{# 判断当前评论是否是子评论 如果是需要渲染对应的评论人名#} {% if comment.parent_id %} <p>@{{ comment.parent.user.username }}</p> {% endif %}
3.
{% block content %} {# 评论楼渲染开始#} {# #3楼 2020-05-14 14:11 代码一字狂#} <div> <ul class="list-group"> {% for comment in comment_list %} <li class="list-group-item"> <span>#{{ forloop.counter }}楼</span> <span>{{ comment.comment_time|date:'Y-m-d h:i:s' }}</span> <span>{{ comment.user.username }}</span> <span><a class="pull-right reply" username="{{ comment.user.username }}" comment_id="{{ comment.pk }}">回复</a></span> <div> {# 判断当前评论是否是子评论 如果是需要渲染对应的评论人名#} {% if comment.parent_id %} <p>@{{ comment.parent.user.username }}</p> {% endif %} {{ comment.content }} </div> </li> {% endfor %} </ul> </div> {# 评论楼渲染结束#} {# 文章评论样式开始 #} {% if request.user.is_authenticated %} <div> <p><span class="glyphicon glyphicon-comment"></span>发表评论</p> <div> <textarea name="comment" id="id_comment" cols="60" rows="10" ></textarea> </div> <button class="btn btn-primary" id="id_submit">提交评论</button> <span style="color: red" id="errors"></span> </div> {% else %} <li><a href="{% url 'reg' %}">注册</a></li> <li><a href="{% url 'login' %}">登陆</a></li> {% endif %} {# 文章评论样式结束 #} {% endblock %}
{% block js %} <script> // 设置一个全局的parentID字段 let parentId = null; // 用户点击评论按钮朝后端发送ajax请求 $('#id_submit').click(function () { // 获取用户评论的内容 let conTent = $('#id_comment').val(); // 判断当前评论是否是子评论 如果是 需要将我们之前手动渲染的@username去除 if(parentId){ // 找到\n对应的索引 然后利用切片 但是前片顾头不顾尾 所以索引+1 let indexNum = conTent.indexOf('\n') + 1; conTent = conTent.slice(indexNum) // 将indexNum之前的所有数据切除 只保留后面的部分 } $.ajax({ url:'/comment/', type:'post', data:{ 'article_id':'{{ article_obj.pk }}', 'content':conTent, // 如果parantId没有值 那么就是null 后端存储null没有任何关系 'parent_id':parentId, 'csrfmiddlewaretoken':'{{ csrf_token }}' }, success:function (args) { if(args.code ==1000){ $('#error').text(args.msg) // 将评论框里面的内容清空 $('#id_comment').val(''); // 临时渲染评论楼 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); // 清空全局的parentId parentId = null; } } }) }) // 给回复按钮绑定点击事件 $('.reply').click(function () { // 需要评论对应的评论人姓名 还需要评论的主键值 // 获取用户名 let commentUserName = $(this).attr('username'); // 获取主键值 直接修改全局 parentId = $(this).attr('comment_id'); // 拼接信息塞给评论框 $('#id_comment').val('@' + commentUserName + '\n').focus() }) </script> {% endblock %}
from django.db import transaction def comment(request): # 自己也可以给自己的文章评论内容 if request.is_ajax(): if request.method == 'POST': back_dic = {'code': 1000, 'msg': ''} if request.user.is_authenticated: article_id = request.POST.get('article_id') content = request.POST.get('content') parent_id = request.POST.get('parent_id') # 直接操作评论表,存储数据(操作两张表) with transaction.atomic(): models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1) models.Comment.objects.create(user=request.user,article_id=article_id,content=content,parent_id=parent_id) back_dic['msg'] = '评论成功' else: back_dic['code'] = 1001 back_dic['msg'] = '用户未登陆' return JsonResponse(back_dic)