在Django中如何为问答论坛的答案实现点赞功能?
实现答案点赞功能的完整方案
嘿,我来帮你搞定这个答案点赞功能!结合你给出的模型代码,我整理了一套完整的实现方案,一步步来:
1. 完善模型定义
首先得把你的Upvote模型补全,还要加约束防止同一个用户给同一个答案重复点赞,同时给Answer模型加几个实用方法方便后续调用:
from django.db import models from django.contrib.auth.models import User from .models import Question # 假设Question和Answer在同一个app下 class Answer(models.Model): ans = models.TextField() img = models.CharField(max_length=100) created_by = models.ForeignKey(User, related_name='answers', on_delete=models.CASCADE) created_on = models.DateTimeField(auto_now_add=True) ques = models.ForeignKey(Question, related_name='answers', on_delete=models.CASCADE) # 可选:添加此字段用信号自动更新,提升查询性能 # upvote_count = models.IntegerField(default=0) def __str__(self): return self.ans[:50] # 显示前50字,避免太长影响可读性 def get_upvote_count(self): # 快速获取该答案的总点赞数 return self.upvotes.filter(upvote=True).count() def user_has_upvoted(self, user): # 判断当前登录用户是否已给这个答案点过赞 return self.upvotes.filter(upvote_by=user, upvote=True).exists() class Upvote(models.Model): upvote = models.BooleanField(default=True) # 默认设为True,创建即代表点赞 upvote_by = models.ForeignKey(User, related_name='answer_upvotes', on_delete=models.CASCADE) answer = models.ForeignKey(Answer, related_name='upvotes', on_delete=models.CASCADE) created_on = models.DateTimeField(auto_now_add=True) class Meta: # 核心约束:一个用户只能给一个答案点一次赞 unique_together = ('upvote_by', 'answer') def __str__(self): return f"{self.upvote_by.username} 点赞了:{self.answer.ans[:20]}"
模型修改说明:
- 给所有外键加了
on_delete=models.CASCADE,这是Django 2.0+的必填项,避免关联对象被删除时出现数据库错误 - 把
related_name改成复数形式(比如answers),更符合语义,方便反向查询 - 给
Answer加了两个实用方法:get_upvote_count拿点赞数,user_has_upvoted判断用户是否已点赞 Upvote模型里通过unique_together确保不会出现重复点赞的情况
2. 写处理点赞的视图
接下来写一个处理点赞/取消点赞的视图,用Ajax请求来做,这样不用刷新整个页面:
from django.http import JsonResponse from django.contrib.auth.decorators import login_required from .models import Answer, Upvote @login_required def toggle_upvote(request): if request.method == 'POST': answer_id = request.POST.get('answer_id') try: answer = Answer.objects.get(id=answer_id) # 用get_or_create判断用户是否已点赞:不存在则创建(点赞),存在则删除(取消) upvote, created = Upvote.objects.get_or_create(upvote_by=request.user, answer=answer) if not created: upvote.delete() is_upvoted = False else: is_upvoted = True # 返回最新的状态和点赞数给前端 return JsonResponse({ 'success': True, 'upvote_count': answer.get_upvote_count(), 'is_upvoted': is_upvoted }) except Answer.DoesNotExist: return JsonResponse({'success': False, 'message': '找不到这个答案哦'}) return JsonResponse({'success': False, 'message': '请求方式不对呀'})
3. 配置URL路由
在你的app的urls.py里加一条路由,让前端能访问到这个视图:
from django.urls import path from . import views urlpatterns = [ # 你的其他路由... path('toggle-upvote/', views.toggle_upvote, name='toggle_upvote'), ]
4. 前端模板实现
最后在答案的展示模板里加点赞按钮,用jQuery处理点击事件,实时更新状态:
{% for answer in question.answers.all %} <div class="answer-card"> <p>{{ answer.ans }}</p> <div class="upvote-area"> <button class="upvote-btn" data-answer-id="{{ answer.id }}"> {% if answer.user_has_upvoted(user) %} <i class="fas fa-thumbs-up"></i> 已点赞 {% else %} <i class="far fa-thumbs-up"></i> 点赞 {% endif %} </button> <span class="upvote-num">{{ answer.get_upvote_count }}</span> </div> </div> {% endfor %} <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script> <script> $(document).ready(function() { $('.upvote-btn').click(function(e) { e.preventDefault(); const answerId = $(this).data('answer-id'); const $btn = $(this); const $count = $(this).siblings('.upvote-num'); $.ajax({ url: '{% url "toggle_upvote" %}', method: 'POST', data: { answer_id: answerId, csrfmiddlewaretoken: '{{ csrf_token }}' // 别忘了加CSRF令牌 }, success: function(res) { if (res.success) { // 更新按钮状态和点赞数 if (res.is_upvoted) { $btn.html('<i class="fas fa-thumbs-up"></i> 已点赞'); } else { $btn.html('<i class="far fa-thumbs-up"></i> 点赞'); } $count.text(res.upvote_count); } else { alert(res.message || '操作失败啦'); } }, error: function() { alert('网络有点问题,稍后再试吧'); } }); }); }); </script>
额外优化小技巧
如果你的网站用户量比较大,每次查询点赞数都去查Upvote表会有点慢,可以给Answer加个upvote_count字段,用Django信号自动更新:
# 在models.py末尾加信号 from django.db.models.signals import post_save, post_delete from django.dispatch import receiver @receiver(post_save, sender=Upvote) @receiver(post_delete, sender=Upvote) def update_answer_upvote_count(sender, instance, **kwargs): answer = instance.answer answer.upvote_count = answer.get_upvote_count() answer.save()
这样以后获取点赞数直接用answer.upvote_count就可以了,不用每次都去统计,性能会好很多~
内容的提问来源于stack exchange,提问作者vinoth kumar
相关产品推荐
相关产品推荐

