关于本站
1、基于Django+Bootstrap开发
2、主要发表本人的技术原创博客
3、本站于 2015-12-01 开始建站
【2018-05-23 ● 注】
这个点赞功能有点老旧,而且存在一些bug。近期我整理出了一个教程,里面重新写了点赞功能。包括ajax等等。
https://spac
参考其中的第28节和第29节。后面我更新这个应用的源码。
【以下是原文】
近期因一个项目写了一个点赞的应用,分享一下(源码地址:https://github.com/HaddyYang/django-likes)。
先思而后行,把大致的实现方法想清楚(现在是讲清楚)再开始写代码。
首先,这个点赞是可以给评论、文章等点赞。而且一般情况,需要可以点赞也可以取消点赞。
那么就需要一个主表和一个明细表。
主表是记录哪个对象被点赞的次数。明细表是记录谁什么时候点赞了或者取消点赞了。这两个表应该设计成一对多的关系,在Django中就是两个模型加一个外键即可。
大致模型在形成了,前端页面可以通过该模型获取对应的点赞数量。可以通过ajax提交点赞请求或者取消点赞请求。获取点赞的数量可以不用写处理方法,直接使用即可。而处理点赞和取消点赞的请求就需要写相关的处理方法。考虑到是ajax的方法提交请求的,那么后台处理数据需要用json的数据结构返回结果比较适合。
这里点赞可以给其他任意对象实现点赞功能,那么需要引入ContentType这个东西。
如果对数据库开发比较好的人员就很容易明白这个是什么作用。简单来说,就是给主表加两个字段,一个是对象类型,一个是对象的ID。这样就可以以不变应万变。不管你对象是什么,只要填这两个字段就可以区分清楚。
大致模型(models.py文件)如下:
#coding:utf-8 __author__ = 'Haddy Yang(Ysh)' __start_date__ = '2016-06-12' """ likes app """ from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.auth.models import User #likes number model class Likes(models.Model): #content type content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = GenericForeignKey( ct_field="content_type", fk_field="object_id" ) #likes number likes_num = models.IntegerField(default = 0) def __unicode__(self): return u'%s:%s(%s)' % (self.content_type, self.object_id, self.likes_num) #likes detail recode class LikesDetail(models.Model): likes = models.ForeignKey(Likes) user = models.ForeignKey(User) is_like = models.BooleanField(default = False) pub_date = models.DateTimeField(auto_now=True) class Meta: ordering=['-pub_date']
Likes就是对应主表,里面有ContentType两个字段和记录点赞数量一个字段。
LikesDetail是对应明细表,记录哪个用户对哪个对象点赞或者取消点赞。这里的用户我使用Django自带的用户系统。你可以对应修改一下,也可以修改成不用登录用户就可以点赞。
处理方法这里一开始写了两个。其中一个是获取点赞的数量,这个用处不大,可以直接通过模型获取到数量。这个可以忽略(有兴趣可以看源码)。主要的处理方法就是处理点赞和取消点赞。具体代码(views.py文件)如下:
#coding:utf-8
__author__ = 'Haddy Yang(Ysh)'
__start_date__ = '2016-06-12'
"""
likes views
"""
from django.shortcuts import render
from django.http import HttpResponse
import json
from likes.models import Likes, LikesDetail
from django.contrib.contenttypes.models import ContentType
#导入likes下自定义的装饰器(decorator.py文件)
from likes.decorator import check_login, check_request
@check_login
@check_request('type', 'id', 'direct')
def likes_change(request):
u"""处理改变点赞状态
Method: GET
params:
type : object type
id : object id
direct: -1 or 1 (add like or remove like)
return: json
"""
#创建json对象需要的数据
data = {}
data['status'] = 200
data['message'] = u'ok'
data['nums'] = 0
#获取数据和对应的对象
obj_type = request.GET.get('type')
obj_id = request.GET.get('id')
user = request.user
direct = 1 if request.GET.get('direct') == '1' else -1
c = ContentType.objects.get(model = obj_type)
#获取Likes对象
try:
l = Likes.objects.get(content_type = c, object_id = obj_id)
except Exception, e:
#没有获取到对象,则新增一个Likes对象
l = Likes(content_type = c, object_id = obj_id)
data['nums'] = l.likes_num
#获取Likes明细对象
try:
detail = LikesDetail.objects.get(likes = l, user = user)
except Exception, e:
detail = LikesDetail(likes = l, user = user, is_like = False)
liked = 1 if detail.is_like else -1
#判断是否赞过,或者取消赞
if liked == direct:
data['status'] = 403
data['message'] = u'Invalid operation'
else:
#更新记录
l.likes_num += direct
if l.likes_num < 0:
l.likes_num = 0
l.save()
data['nums'] = l.likes_num
#修改明细
detail.is_like = direct == 1
detail.save()
#返回结果
return HttpResponse(json.dumps(data), content_type="application/json")大致实现方法是:通过GET请求提交三个参数(type, id, direct)。
type是对应要给某个对象点赞的模型名称;
id就是这个对象的id值;
direct有两个值:-1和1,1代表点赞,-1代表取消点赞。
然后根据这3个参数获取Likes模型对象,判断是否有对应的记录,若没有则创建对象。
获取到Likes对象之后,再根据其id获取对应用户的明细记录。
这里的明细只控制一条明细。因为这个明细信息全部记录没什么作用。而且比较容易产生点赞行为,导致数据库数据增长快。所以这里处理成只对一条明细读写。
另外,眼睛尖的人可能发现我在这个方法上面加了两个装饰器。一个是检查是否有登录用户的;一个是检查GET请求提供的参数是否齐全。这两个装饰器的代码写在这个应用目录下的decorator.py文件。代码如下:
#coding:utf-8
__author__ = 'Haddy Yang(Ysh)'
__start_date__ = '2016-06-12'
"""
likes decorators
"""
from django.http import HttpResponse
import json
#装饰器,检测是否有登录用户
def check_login(func):
u"""检查是否登录用户"""
def warpper(request):
if request.user.is_authenticated():
#若有登录,则继续执行方法
return func(request)
else:
#若未登录,不处理
data = {}
data['status'] = 401
data['message'] = u'no login'
return HttpResponse(json.dumps(data), content_type="application/json")
return warpper
#装饰器,检查request参数是否齐全
def check_request(*params):
def __check_request(func):
def warpper(request):
#print params
#遍历参数
for param in params:
#print param
#判断参数是否存在
if not request.GET.has_key(param):
data = {}
data['status'] = 402
data['message'] = u'no params:%s' % param
return HttpResponse(json.dumps(data), content_type="application/json")
#参数都存在,则继续执行
return func(request)
return warpper
return __check_request这两个装饰器也是比较典型,一个是普通的装饰器;一个是装饰器可以带参数(就像函数带参数一样的使用)。同样,这两个装饰器如果不符合条件的话,就返回json数据。若符合条件,就继续执行。
其他设置可以看看源码,例如urls。而且这个主要使用ajax获取数据,所以不需要创建html页面模版。
可以在我的github上下载源码:https://github.com/HaddyYang/django-likes
1、把应用复制到你的Django项目中。
2、设置settings.py文件,引入该应用。
INSTALLED_APPS = [ 'likes', #加入这个 #others... ]
3、添加链接路由到项目的路由中,打开项目的urls.py文件。
urlpatterns = [
url(r'^admin/', admin.site.urls),
#打开Django项目的urls.py文件,加入这个
url(r'^likes/', include('likes.urls')),
]4、同步更新一下数据库。
python manager.py makemigrations python manager.py migrate
5、然后你对应修改前端页面。例如通过likes的对象模型获取点赞数量、用ajax方法提交点赞请求或者取消点赞。
/likes/likes_change?type=blog&id=1&direct=1
这个是给对象为blog,id为1的对象点赞+1。
/likes/likes_change?type=blog&id=1&direct=-1
这个是给对象为blog,id为1的对象取消点赞。
github源码地址:https://github.com/HaddyYang/django-likes
igoab1990@163.com
谢谢分享
2018-05-24 19:23 回复