效果:可以对评论和对评论的回复进行点赞
因为点赞功能应该属于一个比较独立的功能键,所以新创建一个app:likes
python manage.py startapp likes
likes/models.py中创建LikeCount模型类,创建LikeRecord模型类
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User
class LikeCount(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
liked_num = models.IntegerField(default=0)
class LikeRecord(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
user = models.ForeignKey(User, on_delete=models.CASCADE)
liked_time = models.DateTimeField(auto_now_add=True)
在settings中注册likes这个app
迁移数据库
写前端代码:
blog_detail.html中在评论下面增加图标:
效果:
增加blog.css样式:
效果:
然后调一下位置用padding
使点赞之后大拇指图变成红色:
先设置样式颜色
然后修改blog\_detail.html中的点击事件:
写urls.py文件在likes中:
然后将该url加入到总的路由中:
把上面设置的东西填充到前端写好的结构中:
增加喜欢按钮图标样式:
增加后台处理函数
# likes/views.py
from django.shortcuts import render
from django.contrib.contenttypes.models import ContentType
from django.http import JsonResponse
from django.db.models import ObjectDoesNotExist
from .models import LikeCount, LikeRecord
def ErrorResponse(code, message):
data = {}
data['status'] = 'ERROR'
data['code'] = code
data['message'] = message
return JsonResponse(data)
def SuccessResponse(liked_num):
data = {}
data['status'] = 'SUCCESS'
data['liked_num'] = liked_num
return JsonResponse(data)
def like_change(request):
# 获取数据
user = request.user
if not user.is_authenticated:
return ErrorResponse(400, 'you were not login')
content_type = request.GET.get('content_type')
object_id = int(request.GET.get('object_id'))
try:
content_type = ContentType.objects.get(model=content_type)
model_class = content_type.model_class()
model_obj = model_class.objects.get(pk=object_id)
except ObjectDoesNotExist:
return ErrorResponse(401, 'object not exist')
# 处理数据
if request.GET.get('is_like') == 'true':
# 要点赞
like_record, created = LikeRecord.objects.get_or_create(content_type=content_type, object_id=object_id, user=user)
if created:
# 未点赞过,进行点赞
like_count, created = LikeCount.objects.get_or_create(content_type=content_type, object_id=object_id)
like_count.liked_num += 1
like_count.save()
return SuccessResponse(like_count.liked_num)
else:
# 已点赞过,不能重复点赞
return ErrorResponse(402, 'you were liked')
else:
# 要取消点赞
if LikeRecord.objects.filter(content_type=content_type, object_id=object_id, user=user).exists():
# 有点赞过,取消点赞
like_record = LikeRecord.objects.get(content_type=content_type, object_id=object_id, user=user)
like_record.delete()
# 点赞总数减1
like_count, created = LikeCount.objects.get_or_create(content_type=content_type, object_id=object_id)
if not created:
like_count.liked_num -= 1
like_count.save()
return SuccessResponse(like_count.liked_num)
else:
return ErrorResponse(404, 'data error')
else:
# 没有点赞过,不能取消
return ErrorResponse(403, 'you were not liked')
然后在likes中创建templatetags的python包,新增likes_tags.py文件
# likes_tags.py
from django import template
from django.contrib.contenttypes.models import ContentType
from ..models import LikeCount, LikeRecord
register = template.Library()
@register.simple_tag
def get_like_count(obj):
content_type = ContentType.objects.get_for_model(obj)
like_count, created = LikeCount.objects.get_or_create(content_type=content_type, object_id=obj.pk)
return like_count.liked_num
@register.simple_tag(takes_context=True)
def get_like_status(context, obj): # 加入context可以获取所在模板页面的所有变量
"""
获取是否点赞的状态
"""
content_type = ContentType.objects.get_for_model(obj)
user = context['user']
if not user.is_authenticated: # 判断是否登录
return ''
if LikeRecord.objects.filter(content_type=content_type, object_id=obj.pk, user=user).exists():
return 'active'
else:
return ''
@register.simple_tag
def get_content_type(obj): # 用于让前端使用模板标签
content_type = ContentType.objects.get_for_model(obj)
return content_type.model
然后再blog_detail.html中导入并渲染likes_tags中的提取到的内容:
在博文列表中增加点赞数量:在blog\_list.html中加载likes\_tags:
继续完善点赞功能:blog\_detail.html的代码
{% extends 'base.html' %}
{% block title %}{{ blog.title }}{% endblock %}
{% block nav_blog_active %}active{% endblock %}
{% load static %}
{% load comment_tags %}
{% load likes_tags %}
{% block header_extends %}
<link rel="stylesheet" href="{% static 'blog/blog.css' %}">
<script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script>
<script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>
{% endblock %}
{# 页面内容 #}
{% block content %}
<div class="container">
<div class="row">
<div class="col-xs-10 col-xs-offset-1">
<h3>{{ blog.title }}</h3>
<ul class="blog-info-description">
<li>作者:{{ blog.author }}</li>
<li>分类:<a href="{% url 'blogs_with_type' blog.blog_type.pk %}">{{ blog.blog_type }}</a></li>
<li>发表日期:{{ blog.created_time|date:"Y-m-d H:i:s" }}</li>
<li>阅读量:{{ blog.get_read_num }}</li>
<li>评论数:{% get_comment_count blog %}</li>
</ul>
<div class="blog-content">{{ blog.content|safe }}</div>
<div class="like" onclick="likeChange(this, '{% get_content_type blog %}', {{ blog.pk }})">
<span class="glyphicon glyphicon-thumbs-up {% get_like_status blog %}"></span>
<span class="liked-num">{% get_like_count blog %}</span>
<span>喜欢</span>
</div>
<div class="blog-more">
<p>上一篇:
{% if previous_blog %}
<a href="{% url 'blog_detail' previous_blog.pk %}">{{ previous_blog.title }}</a>
{% else %}
没有了
{% endif %}
</p>
<p>下一篇:
{% if next_blog %}
<a href="{% url 'blog_detail' next_blog.pk %}">{{ next_blog.title }}</a>
{% else %}
没有了
{% endif %}
</p>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-10 col-xs-offset-1">
<div class="comment-area">
<h3 class="comment-area-title">提交评论</h3>
{% if user.is_authenticated %}
<form id="comment_form" action="{% url 'update_comment' %}" method="POST" style="overflow:hidden">
<label>{{ user.username }},欢迎评论~</label>
<div id="reply_content_container" style="display:none;">
<p id="reply_title">回复:</p>
<div id="reply_content"></div>
</div>
{% csrf_token %}
{% get_comment_form blog as comment_form %}
{% for field in comment_form %}
{{ field }}
{% endfor %}
<span id="comment_error" class="text-danger pull-left"></span>
<input type="submit" value="评论" class="btn btn-primary pull-right">
</form>
{% else %}
您尚未登录,登录之后方可评论~
<a class="btn btn-primary" href="{% url 'login' %}?from={{ request.get_full_path }}">登录</a>
<span> or </span>
<a class="btn btn-danger" href="{% url 'register' %}?from={{ request.get_full_path }}">注册</a>
{% endif %}
</div>
<div class="comment-area">
<h3 class="comment-area-title">评论列表</h3>
<div id="comment_list">
{% get_comment_list blog as comments %}
{% for comment in comments %}
<div id="root_{{ comment.pk }}" class="comment">
<span>{{ comment.user.username }}</span>
<span>({{ comment.comment_time|date:"Y-m-d H:i:s" }}):</span>
<div id="comment_{{ comment.pk }}">
{{ comment.text|safe }}
</div>
<div class="like" onclick="likeChange(this, '{% get_content_type comment %}', {{ comment.pk }})">
<span class="glyphicon glyphicon-thumbs-up {% get_like_status comment %}"></span>
<span class="liked-num">{% get_like_count comment %}</span>
</div>
<a href="javascript:reply({{ comment.pk }});">回复</a>
{% for reply in comment.root_comment.all %}
<div class="reply">
<span>{{ reply.user.username }}</span>
<span>({{ reply.comment_time|date:"Y-m-d H:i:s" }})</span>
<span>回复</span>
<span>{{ reply.reply_to.username }}:</span>
<div id="comment_{{ reply.pk }}">
{{ reply.text|safe }}
</div>
<div class="like" onclick="likeChange(this, '{% get_content_type reply %}', {{ reply.pk }})">
<span class="glyphicon glyphicon-thumbs-up {% get_like_status reply %}"></span>
<span class="liked-num">{% get_like_count reply %}</span>
</div>
<a href="javascript:reply({{ reply.pk }});">回复</a>
</div>
{% endfor %}
</div>
{% empty %}
<span id="no_comment">暂无评论</span>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block script_extends %}
<script type="text/javascript">
function reply(reply_comment_id){
// 设置值
$('#reply_comment_id').val(reply_comment_id);
var html = $("#comment_" + reply_comment_id).html();
$('#reply_content').html(html);
$('#reply_content_container').show();
$('html').animate({scrollTop: $('#comment_form').offset().top - 60}, 300, function(){
CKEDITOR.instances['id_text'].focus();
});
}
function numFormat(num){
return ('00' + num).substr(-2);
}
function timeFormat(timestamp){
var datetime = new Date(timestamp * 1000);
var year = datetime.getFullYear();
var month = numFormat(datetime.getMonth() + 1);
var day = numFormat(datetime.getDate());
var hour = numFormat(datetime.getHours());
var minute = numFormat(datetime.getMinutes());
var second = numFormat(datetime.getSeconds());
return year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second;
}
$("#comment_form").submit(function(){
// 判断是否为空
$("#comment_error").text('');
if(CKEDITOR.instances["id_text"].document.getBody().getText().trim()==''){
$("#comment_error").text('评论内容不能为空');
return false;
}
// 更新数据到textarea
CKEDITOR.instances['id_text'].updateElement();
// 异步提交
$.ajax({
url: "{% url 'update_comment' %}",
type: 'POST',
data: $(this).serialize(),
cache: false,
success: function(data){
console.log(data);
if(data['status']=="SUCCESS"){
if($('#reply_comment_id').val()=='0'){
// 插入评论
var comment_html = '<div id="root_' + data['pk'] + '" class="comment"><span>' + data['username'] + '</span><span> (' + timeFormat(data['comment_time']) + '):</span><div id="comment_' + data['pk'] + '">' + data['text'] + '</div><a href="javascript:reply(' + data['pk'] + ');">回复</a></div>';
$("#comment_list").prepend(comment_html);
}else{
// 插入回复
var reply_html ='<div class="reply"><span>' + data['username'] + '</span><span> (' + timeFormat(data['comment_time']) + ')</span><span> 回复 </span><span>' + data['reply_to'] + ':</span><div id="comment_' + data['pk'] + '">' + data['text'] + '</div><a href="javascript:reply(' + data['pk'] + ');">回复</a></div>';
$("#root_" + data['root_pk']).append(reply_html);
}
// 清空编辑框的内容
CKEDITOR.instances['id_text'].setData('');
$('#reply_content_container').hide();
$('#reply_comment_id').val('0');
$('#no_comment').remove();
$("#comment_error").text('评论成功');
}else{
// 显示错误信息
$("#comment_error").text(data['message']);
}
},
error: function(xhr){
console.log(xhr);
}
});
return false;
});
function likeChange(obj, content_type, object_id){
var is_like = obj.getElementsByClassName('active').length == 0
$.ajax({
url: "{% url 'like_change' %}",
type: 'GET',
data: {
content_type: content_type,
object_id: object_id,
is_like: is_like
},
cache: false,
success: function(data){
console.log(data)
if(data['status']=='SUCCESS'){
// 更新点赞状态
var element = $(obj.getElementsByClassName('glyphicon'));
if(is_like){
element.addClass('active');
}else{
element.removeClass('active');
}
// 更新点赞数量
var liked_num = $(obj.getElementsByClassName('liked-num'));
liked_num.text(data['liked_num']);
}else{
alert(data['message']);
}
},
error: function(xhr){
console.log(xhr)
}
});
}
</script>
{% endblock %}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。