在我们前两遍文章里,我们已经创建一个比较完善的Article模型,利用Django的通用视图开发了博客的管理后台,实现了文章的增删查改。我们还配置了CKEditor实现博客文章的富文本编辑(包括上传图片和显示代码)。在本文中我们将对该博客做进一步改进,利用AJAX技术实现仿微信评论的点赞功能。当登录用户点击大拇指按钮时,文章总点赞次数在页面无需刷新的情况下自动加1,当用户再次点击大拇指时,取消点赞,总点赞次数自动减1。非登录用户在点击大拇指按钮时,会被要求先登录。本文参考了部分Django By Example的内容。
如果你还没有阅读过本专题,请务必先阅读之前两篇文章:
实现思路
我们在Article模型里增加一个users_like的字段,记录点赞的用户,其与User是多对多的关系(如下所示)。有了这个字段,我们可以使用article.users_like.all查询点赞某篇文章的所有用户,还可以使用article.users_like.count来统计某篇文章的总点赞数。通过使用user.articles_liked.all可以查询某个用户所喜欢的所有文章条目。
# models.py
classArticle(models.Model):
"""文章模型"""
slug = models.SlugField('slug',max_length=60,blank=True)
body = RichTextUploadingField('正文')
pub_date = models.DateTimeField('发布时间',null=True)
.....
users_like = models.ManyToManyField(User,
related_name='articles_liked',blank=True)
当用户在前端页面点击大拇指按钮时,我们希望其触发一个AJAX请求到后台。这个AJAX请求发送的数据应包括文章id和动作(like和unlike)。因为request是全局变量,所以即使Ajax发送的数据里即使不包括用户,我们也可以知道request.user是谁。有了文章id,用户和动作,我们就可以轻易的处理请求,并返回Json格式的状态了。
我们现在需要在urls.py里新增一个名为article_like的url, 专门处理Ajax请求。
#urls.py
fromdjango.urlsimportpath,re_path
from.importviews
# namespace
app_name ='blog'
urlpatterns = [
re_path(r'^article/like/$',
views.article_like,name='article_like'),
]
上述url对应的视图函数如下所示:
# views.py
fromdjango.views.decorators.httpimportrequire_http_methods
fromdjango.contrib.auth.decoratorsimportlogin_required
@login_required
@require_http_methods(["POST"])
defarticle_like(request):
article_id = request.POST.get('id')
action = request.POST.get('action')
ifarticle_idandaction:
try:
article = Article.objects.get(id=article_id)
ifaction =='like':
article.users_like.add(request.user)
else:
article.users_like.remove(request.user)
returnJsonResponse({'status':'ok'})
except:
pass
returnJsonResponse({'status':'fail'})
上面这段代码是这么工作的:
我们使用@login_required和@require_http_methods两个装饰器用户必需要先登录才能点赞,而且只接收通过POST方式发送过来的请求。
如果发送过来的Ajax POST请求包括article_id和action两个参数。我们根据action对数据库进行更新。如果操作成功,就给前端返回Json格式的数据{"status":"ok"}。我们在模型的多对多字段上使用了 Django 提供的或者方法来执行多对多关系的建立和删除, 这个方法并save方法更快捷。
如果用户未登陆或某些操作未成功,我们给前端返回Json数据{"status":"fail"}
模板与Ajax代码
最后我们需要在article_detail模板加入下面2段代码,实现本文所需要的功能。
# blog/template/article_detail.html
第一段为html代码,主要用来展示点赞按钮和总点赞数。我们设置了data-id和data-action两个参数,便于Ajax通过DOM获取文章id和action。我们先判断当前用户是否在已点赞用户清单里,如果在的话,action变为unlike。如果不在的话,action为like。
{% if article.status == "p" %}
发布于{{ article.pub_date | date:"Y-m-j" }}浏览{{ article.views }}次
{% with total_likes=article.users_like.count users_like=article.users_like.all %}
data-action="{% if request.user in users_like %}un{% endif %}like"
class="like button">
class="glyphicon glyphicon-thumbs-up count">{{ total_likes }}
{% endwith %}
{% endif %}
第二段为JS代码, 如下所示。因为我们采用的是POST提交方式,Django默认POST是需要附上csrftoken的。本例中我们在Ajax请求发送前使用了jQuery的cookie库把crsftoken加入到请求头里(见beforeSend属性)。如果你不想要发送csrftoken,你还可以在视图里使用@crsf_exempt装饰器。
{% block js %}
{% endblock %}
上面这段代码是这么工作的。
我们通过JS获取文章id和action两个参数,通过Ajax POST请求发送到处理Ajax请求的url(blog/article/like)。
如果返回的status==ok,我们变换动作(action), 并对总点赞数进行更新(加1或减1)。
如果返回的status不等于ok,要么用户未登陆,要么用户进行了非法操作。此时我们通过设置window.location.href属性强行对用户进行跳转到登录页面。
注意:尽管我们已经在视图里使用了login_required装饰器, 我们还需要在JS代码里写明跳转链接,这样才能实现未登录用户点击点赞按钮后自动跳转到登录页面。这是因为如果你使用AJAX和服务器进行交互沟通,你从服务器得到的只是JSON格式数据,而并不是真的访问那个页面。
实战效果
最后实战效果如下。写了这么多,只是给前端页面增加了个不起眼的点赞按钮。看似简单的东西不一定简单。
小结
我们利用AJAX技术实现了仿微信评论的点赞功能,主要讲解了以下知识点:
利用add和remove方法实现了多对多关系的添加和删除
Ajax和Django视图的交互原理
利用Ajax获取crsftoken,发送POST请求
利用Ajax实现跳转
如果喜欢本文,欢迎点赞或关注我们的微信公众号。
2018.10.18
大江狗
领取专属 10元无门槛券
私享最新 技术干货