Django如何在单一URL下实现电影评论的CRUD功能
如何在单一URL下实现评论的创建、删除、更新与读取功能?
实现目标
在单个视图函数中完成评论的增、删、改、查全流程,全程无需变更URL地址。业务规则:单个用户不可对同一部电影重复发布评论。
现有代码问题梳理
- 模型层
Movie.average_stars()方法存在语法错误,重复写了else关键字 - 视图层仅判断用户是否已评论,已评论就直接返回提示,没有预留编辑、删除的逻辑分支
- 模板层提交按钮未做场景区分,评论列表头像错误引用了全局登录用户对象,导致所有评论头像显示一致
- 原评论排序用Python层反转,数据量大时查询效率低
具体实现方案
核心逻辑:在同一视图内,通过POST请求携带的action参数区分操作类型;页面加载时预查询当前用户对当前电影的已有评论,存在则将评论内容填充到表单作为初始值,不存在则渲染空表单。
1. 修复模型层语法错误
先修正Movie模型的平均分计算方法,Comment_movie模型已经配置了user和movie的联合唯一约束,数据库层会自动拦截重复评论,无需额外加重复判断逻辑:
class Movie (models.Model): id = models.CharField (primary_key = True, editable = False, validators = [alphanumeric], max_length = 9999) def get_comments (self): return Comment_movie.objects.filter (movie_id = self.id) def average_stars (self): comments = self.get_comments () n_comments = comments.count () if n_comments: return sum ([comment.stars for comment in comments]) / n_comments return 0 class Comment_movie (models.Model): class Meta: unique_together = ('user','movie',) comment = models.TextField (max_length = 1000) stars = models.FloatField ( blank = False, null = False, default = 0, validators = [MinValueValidator (0.0), MaxValueValidator (10.0)] ) user = models.ForeignKey (CustomUser, on_delete = models.CASCADE) movie = models.ForeignKey (Movie, on_delete = models.CASCADE) created_at = models.DateTimeField (default = datetime.now) updated_at = models.DateTimeField (auto_now = True)
2. 重写视图函数逻辑
给视图加登录校验,区分POST操作类型,分别处理新建、更新、删除逻辑,表单直接绑定用户已有评论实例,存在就走更新逻辑,不存在就走新建逻辑,不需要单独写两套保存代码:
from django.shortcuts import get_object_or_404, redirect, render from django.contrib.auth.decorators import login_required import requests from .models import Movie, Comment_movie from .forms import Comment_movie_CreateForm from django.conf import settings TMDB_API_KEY = settings.TMDB_API_KEY @login_required def view_movie_detail(request, movie_id): movie, created = Movie.objects.get_or_create(id=movie_id) # 单次查询拿到当前用户对这部电影的已有评论,减少重复数据库请求 user_comment = Comment_movie.objects.filter(user=request.user, movie=movie).first() if request.method == "POST": action = request.POST.get("action") # 删除操作分支 if action == "delete" and user_comment: user_comment.delete() return redirect('view_movie_detail', movie_id=movie_id) # 新建/更新操作分支,直接绑定实例自动适配场景 form = Comment_movie_CreateForm(request.POST, instance=user_comment) if form.is_valid(): comment = form.save(commit=False) comment.user = request.user comment.movie = movie comment.save() return redirect('view_movie_detail', movie_id=movie_id) else: # GET请求,有评论就把评论内容预填到表单 form = Comment_movie_CreateForm(instance=user_comment) # 拉取TMDB数据逻辑保持不变 data = requests.get(f"https://api.themoviedb.org/3/movie/{movie_id}?api_key={TMDB_API_KEY}&language=en-US") recommendations = requests.get(f"https://api.themoviedb.org/3/movie/{movie_id}/recommendations?api_key={TMDB_API_KEY}&language=en-US") # 直接在数据库层做倒序排序,查询效率更高 comments = Comment_movie.objects.filter(movie_id=movie_id).order_by("-created_at") average = movie.average_stars() context = { "data": data.json(), "recommendations": recommendations.json(), "type": "movie", "comments": comments, "average": average, "form": form, "user_comment": user_comment, } return render(request, "Movie/movie_detail.html", context)
原有表单类无需修改,可直接复用。
3. 调整模板代码
根据用户是否已有评论显示对应操作按钮,修复评论列表头像显示bug,删除操作增加前端二次确认避免误删:
{% if user.is_anonymous %} <a class="nav-link" href="{% url 'account_login' %}">Login</a> <hr> {% else %} <h2>Comments</h2> {% if form.errors %} <div class = "error_list"> {% for errors in form.errors.values %} {% for error in errors %} {{ error }}<br> {% endfor %} {% endfor %} </div> {% endif %} <form method="POST" enctype="multipart/form-data"> {% csrf_token %} {{ form }} {% if user_comment %} <button type="submit">保存修改</button> <button type="submit" name="action" value="delete" onclick="return confirm('确定要删除这条评论吗?')">删除评论</button> {% else %} <button type="submit">发布评论</button> {% endif %} </form> {% endif %} <hr> <table> <tr> <th>头像</th> <th>昵称</th> <th>发布时间</th> <th>评论内容</th> <th>评分</th> </tr> {% for c in comments %} <tr> <td><img class="rounded-circle" src="{{ c.user.avatar.url }}" alt="用户头像" width="100" height="100"></td> <td>{{ c.user.nickname }}</td> <td>{{ c.created_at }} </td> <td>{{ c.comment }}</td> <td><h2 class = "rate" style="--rating:{{c.stars}}">{{c.stars}}</h2></td> </tr> {% endfor %} </table> <hr>
逻辑说明
- 所有POST请求都提交到当前URL,通过
action参数区分删除、保存操作,全程无URL跳转 - 用户已有评论时,表单自动填充原有内容,提交后直接更新对应记录,不会生成新评论,完全符合「单用户单电影仅一条评论」的规则
- 修复了原代码中所有评论头像显示为当前登录用户头像的bug,改为显示对应评论发布者的头像
内容的提问来源于stack exchange,提问作者user19553515
相关产品推荐
相关产品推荐

