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

Django实现单用户单影片单条评论限制及评论跨影片显示问题排查

问题根因排查

  1. 评论查询未过滤对应电影:views.py中movies_detail函数里的all_reviews = Review.objects.all().order_by('movie')是查询了全站所有评论,没有过滤当前页面的电影,哪怕模板做判断也效率极低且容易出错。
  2. 模板变量引用错误:模板判断用的movie.title根本没有在视图传递的上下文里,上下文当前电影变量叫articles,判断条件永远不生效,导致所有评论都渲染出来。
  3. check_user_review函数逻辑错误:现有循环只要第一个评论不是当前用户的就直接返回True,逻辑完全错误,无法正确判断用户是否已经评过当前电影。
  4. 缺少数据库层约束:没有在Review模型加联合唯一索引,极端情况下仍可能出现同一用户同一电影多条评论的问题。

解决方案

第一步:修改models.py,添加联合唯一约束

from django.db import models
from django.contrib.auth.models import User

# 确保你自己定义了CATEGORY_CHOICE,这里不影响现有逻辑
CATEGORY_CHOICE = (
    # 你的分类定义
)

class Play(models.Model):
    title = models.CharField(max_length=150)
    plot = models.TextField(max_length=900)
    slug = models.SlugField(unique=True, null=True)
    cast = models.CharField(max_length=500, default=0)
    category = models.CharField(choices=CATEGORY_CHOICE, max_length=10)
    image = models.ImageField(upload_to='movie')
    year = models.DateTimeField()
    author = models.ForeignKey(User, default=None, on_delete=models.CASCADE)

    def __str__(self):
        return self.title

class Review(models.Model):
    user = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
    # 添加related_name方便反向查询当前电影的所有评论
    movie = models.ForeignKey(Play, blank=True, null=True, on_delete=models.CASCADE, related_name='reviews')
    text = models.TextField()
    # 补全你用到的点赞字段
    likes = models.ManyToManyField(User, related_name='review_likes', blank=True)

    class Meta:
        # 数据库层面兜底:同一用户对同一电影只能发一条评论
        unique_together = ('user', 'movie')

    # 补全点赞计数方法
    def count_likes(self):
        return self.likes.count()

第二步:修改views.py,优化查询和校验逻辑

from django.shortcuts import render, redirect, reverse
from .models import Play, Review
from django.contrib.auth.decorators import login_required
from . import forms
from django.http import HttpResponseRedirect

def movies_list(request):
    article = Play.objects.all().order_by('year')
    return render(request, "articles/movies_list.html", {"articles":article})

def movies_detail(request, slug):
    current_movie = Play.objects.get(slug=slug)
    # 仅查询当前电影的评论,无需查询全站评论
    all_reviews = current_movie.reviews.all().order_by('-id')
    isReviewAllowed = check_user_review(request, current_movie)
    # 修改变量名避免歧义
    return render(request, "articles/movies_detail.html", {"current_movie": current_movie,'all_reviews': all_reviews, 'isReviewAllowed': isReviewAllowed})

@login_required(login_url="/accounts/login/")
def article_create(request):
    if request.method == "POST":
        form = forms.CreatePlay(request.POST, request.FILES)
        if form.is_valid():
            instance = form.save(commit=False)
            instance.author = request.user
            instance.save()
            return redirect("movies:home")
    else:
        form = forms.CreatePlay()
    return render(request, "articles/article_create.html", {"form":form})

@login_required(login_url="/accounts/login/")
def sub_review(request, slug):
    current_movie = Play.objects.get(slug=slug)
    # 提交前先校验,避免用户绕过前端直接重复提交评论
    if Review.objects.filter(user=request.user, movie=current_movie).exists():
        return redirect("movies:detail", slug=slug)
    if request.method == "POST":
        form = forms.CreateReview(request.POST)
        if form.is_valid():
            instance = form.save(commit=False)
            instance.user = request.user
            instance.movie = current_movie
            instance.save()
            return redirect("movies:detail", slug=slug)
    else:
        form = forms.CreateReview()
    return render(request, "articles/review.html", {"form":form, "slug":slug})

# 重写校验逻辑,无需循环全表,查询效率大幅提升
def check_user_review(request, current_movie):
    if not request.user.is_authenticated:
        return False
    # 直接查询是否存在当前用户对当前电影的评论
    return not Review.objects.filter(user=request.user, movie=current_movie).exists()

def like_view(request, slug):
    review_id = request.POST.get('review_id')
    if review_id:
        try:
            like = Review.objects.get(id=review_id)
            like.likes.add(request.user)
        except Review.DoesNotExist:
            pass
    return HttpResponseRedirect(reverse('movies:detail', args=[slug]))

第三步:修改movies_detail.html,修正变量引用,删除冗余判断

{% extends 'base.html' %}
{% block content %}
<img src="{{ current_movie.image.url }}" width="200px" height="300" style="display: inline-block;" >
<div id="movie_plot">
<h2>{{current_movie.title}}</h2>
  <p>{{current_movie.plot}}</p>
{{current_movie.category}}
  <p>{{current_movie.cast}}</p>
</div>

{% if isReviewAllowed %}
<div class="isReviewAllowed">
        <div class="nav_button"><a class="new_movie" href="{% url 'movies:review' slug=current_movie.slug %}">Create Review</a></div>
</div>
{% endif %}

{% for review in all_reviews %}
<div class="all_reviews">
<div class="review">
<p><span style="font-weight: normal;">Reviewed by: </span> {{review.user}} </p>
            <p><span style="font-weight: normal;">Description: </span>{{review.text}}</p>
<form style="text-align: center;" action="{% url 'movies:like' slug=current_movie.slug %}" method="post">
{% csrf_token %}
<button style="color: white; background: #104e89;" type="submit" name="review_id" value="{{ review.id }}" class="btn btn-primary btn-sm">Like</button> {{review.count_likes}}
</form>
</div>
</div>
{% endfor %}
{% endblock %}

第四步:执行数据库迁移

修改模型后执行以下命令,将联合唯一约束同步到数据库:

python manage.py makemigrations
python manage.py migrate

内容的提问来源于stack exchange,提问作者Hagai Buachi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 08:15:04