You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django中为匿名用户实现基于IP的点赞点踩按钮

用IP地址替代用户实现Django点赞点踩功能

问题背景

我正在开发一个Django网站,为了吸引更多访客,决定不做用户登录/登出功能。现在需要实现点赞点踩按钮,但要把原本用request.user标识用户的逻辑换成用IP地址,请问该怎么改?以下是我目前基于request.user实现的代码:

原视图代码(views.py,Dislike逻辑类似)

@login_required(login_url="login")
def Like(request, slug):
  add = Add.objects.get(slug=slug)
  
  # 点赞时取消点踩
  is_dislike = False

  for dislike in add.dislikes.all():
    if dislike == request.user:
      is_dislike = True
      break

  if is_dislike:
    add.dislikes.remove(request.user)

  # 处理点赞逻辑
  is_like = False

  for like in add.likes.all():
    if like == request.user:
      is_like = True
      break
  
  if not is_like:
    add.likes.add(request.user)

  if is_like:
    add.likes.remove(request.user)

  next = request.POST.get("next", "/")
  return HttpResponseRedirect(next)

原模板代码(review.html)

<div class="rate-added-rule">
  <form action="{% url 'like' added_data.slug %}" method="POST">
    {% csrf_token %}
    <input type="hidden" name="next" value="{{ request.path }}">
    <button type="submit" class="like-button">
      <i class="far fa-thumbs-up">LIKE<span>VOTES:&nbsp;{{ added_data.likes.all.count }}</span></i>
    </button>
  </form>

  <form action="{% url 'dislike' added_data.slug %}" method="POST">
    {% csrf_token %}
    <input type="hidden" name="next" value="{{ request.path }}">
    <button type="submit" class="dislike-button">
      <i class="far fa-thumbs-down">DISLIKE<span>VOTES:&nbsp;{{ added_data.dislikes.all.count }}</span></i>
    </button>
  </form>
</div>

解决方案

1. 修改模型结构

原来的likes和dislikes是关联User的多对多字段,现在换成基于IP的投票记录,推荐新建一个Vote模型统一管理投票:

# models.py
from django.db import models

class Add(models.Model):
    # 保留你原有的字段,比如slug、内容等
    slug = models.SlugField(unique=True)
    # 移除原来的likes和dislikes字段

class Vote(models.Model):
    LIKE = 1
    DISLIKE = -1
    VOTE_CHOICES = [
        (LIKE, 'Like'),
        (DISLIKE, 'Dislike')
    ]
    add = models.ForeignKey(Add, on_delete=models.CASCADE, related_name='votes')
    ip_address = models.CharField(max_length=45)  # 支持IPv6,最长45字符
    vote_type = models.IntegerField(choices=VOTE_CHOICES)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        unique_together = ('add', 'ip_address')  # 同一IP对同一内容只能投一次票

2. 更新视图逻辑

去掉@login_required装饰器,获取用户真实IP,处理投票逻辑:

# views.py
from django.shortcuts import get_object_or_404, redirect
from .models import Add, Vote

def get_client_ip(request):
    # 获取用户真实IP,兼容反向代理场景
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

def Like(request, slug):
    add = get_object_or_404(Add, slug=slug)
    user_ip = get_client_ip(request)
    next_url = request.POST.get("next", "/")

    # 先取消该IP的点踩记录(如果存在)
    Vote.objects.filter(add=add, ip_address=user_ip, vote_type=Vote.DISLIKE).delete()

    # 切换点赞状态:存在则取消,不存在则添加
    existing_vote = Vote.objects.filter(add=add, ip_address=user_ip, vote_type=Vote.LIKE).first()
    if existing_vote:
        existing_vote.delete()
    else:
        Vote.objects.create(add=add, ip_address=user_ip, vote_type=Vote.LIKE)

    return redirect(next_url)

def Dislike(request, slug):
    add = get_object_or_404(Add, slug=slug)
    user_ip = get_client_ip(request)
    next_url = request.POST.get("next", "/")

    # 先取消该IP的点赞记录(如果存在)
    Vote.objects.filter(add=add, ip_address=user_ip, vote_type=Vote.LIKE).delete()

    # 切换点踩状态:存在则取消,不存在则添加
    existing_vote = Vote.objects.filter(add=add, ip_address=user_ip, vote_type=Vote.DISLIKE).first()
    if existing_vote:
        existing_vote.delete()
    else:
        Vote.objects.create(add=add, ip_address=user_ip, vote_type=Vote.DISLIKE)

    return redirect(next_url)

3. 调整模板显示

修改投票计数逻辑,从Vote模型中统计对应类型的票数:

<div class="rate-added-rule">
  <form action="{% url 'like' added_data.slug %}" method="POST">
    {% csrf_token %}
    <input type="hidden" name="next" value="{{ request.path }}">
    <button type="submit" class="like-button">
      <i class="far fa-thumbs-up">LIKE<span>VOTES:&nbsp;{{ added_data.votes.filter(vote_type=1).count }}</span></i>
    </button>
  </form>

  <form action="{% url 'dislike' added_data.slug %}" method="POST">
    {% csrf_token %}
    <input type="hidden" name="next" value="{{ request.path }}">
    <button type="submit" class="dislike-button">
      <i class="far fa-thumbs-down">DISLIKE<span>VOTES:&nbsp;{{ added_data.votes.filter(vote_type=-1).count }}</span></i>
    </button>
  </form>
</div>

4. 额外优化建议

  • IP处理:如果网站部署在反向代理(如Nginx)后,要确保服务器配置中正确传递用户真实IP,否则会获取到代理服务器的IP。
  • 可选Cookie辅助:如果担心同一IP下多个用户的情况,可以结合Cookie记录设备标识,进一步区分投票主体,但IP仍作为核心标识。

内容的提问来源于stack exchange,提问作者Oh Yeah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 17:15:37