Django中NOT NULL约束失败(Movies_comment.movie_id)问题修复求助
Django评论功能修复:NOT NULL与外键约束失败问题
问题背景
在Django项目中实现Movie模型的评论及评分平均值计算功能时,出现两个关联错误:
NOT NULL constraint failed: Movies_comment.movie_id- 后续触发
FOREIGN KEY constraint failed
关联代码
模型代码
class Movie(models.Model): def get_comments(self): return Comment.objects.filter(movie_id=self.id) def average_stars(self): comments = self.get_comments() n_comments = comments.count() if n_comments == 0: return 0 else: return sum([comment.stars for comment in comments]) / n_comments class Comment(models.Model): comment = models.TextField(max_length=1000) stars = models.FloatField( blank=False, null=False, default=0, validators=[MinValueValidator(0.0), MaxValueValidator(10.0)] ) user = models.OneToOneField(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)
视图代码
def view_movie_detail(request, movie_id): if request.method == "POST": user = request.user comment = request.POST.get("comment") stars = request.POST.get("stars") if not request.user.is_authenticated: user = User.objects.get(id=1) Comment(comment=comment, stars = stars, user=user,movie_id=movie_id).save() return redirect(f"/movie/{movie_id}/") else: 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 = reversed(Movie().get_comments()) average = Movie().average_stars() return render(request, "Movie/movie_detail.html", { "data": data.json(), "recommendations": recommendations.json(), "type": "movie", "comments": comments, "average" : average, })
翻译后的错误回溯
Traceback (most recent call last): File "C:\\Users\\81804\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\django\\db\\utils.py", line 98, in inner return func(*args, **kwargs) 上述异常(NOT NULL约束失败)直接引发了以下异常: File "C:\\Users\\81804\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\django\\core\\handlers\\exception.py", line 55, in inner response = get_response(request) File "C:\\Users\\81804\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\django\\core\\handlers\\base.py", line 197, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "C:\\Users\\81804\\enviroment\\MovieReview\\MovieReview\\Movies\\views.py", line 74, in view_movie_detail Comment(comment=comment, stars = stars, user=user,movie_id=movie_id).save() File "C:\\Users\\81804\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\django\\db\\models\\base.py", line 806, in save self.save_base( File "C:\\Users\\81804\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\django\\db\\models\\base.py", line 857, in save_base updated = self._save_table( File "C:\\Users\\81804\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\django\\db\\models\\base.py", line 1000, in _save_table results = self._do_insert( File "C:\\Users\\81804\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\django\\db\\models\\base.py", line 1041, in _do_insert return manager._insert( File "C:\\Users\\81804\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\django\\db\\models\\manager.py", line 85, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) File "C:\\Users\\81804\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\django\\db\\models\\query.py", line 1434, in _insert return query.get_compiler(using=using).execute_sql(returning_fields) File "C:\\Users\\81804\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\django\\db\\models\\sql\\compiler.py", line 1632, in execute_sql self.connection.ops.fetch_returned_insert_columns( File "C:\\Users\\81804\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\django\\db\\backends\\base\\operations.py", line 205, in fetch_returned_insert_columns return cursor.fetchone() File "C:\\Users\\81804\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\django\\db\\utils.py", line 98, in inner return func(*args, **kwargs) File "C:\\Users\\81804\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\django\\db\\utils.py", line 91, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File "C:\\Users\\81804\\AppData\\Local\\Programs\\Python\\Python38\\lib\\site-packages\\django\\db\\utils.py", line 98, in inner return func(*args, **kwargs) 异常类型:IntegrityError at /movie/616037/ 异常信息:外键约束失败
错误原因
- 外键关联失效:创建Comment时传入的
movie_id对应的Movie实例未在本地数据库中创建——仅通过TMDB API获取数据,但未同步到本地Movie表,导致外键约束校验失败。 - Movie实例调用错误:GET请求中用
Movie().get_comments()创建空实例,会查询所有评论的平均值,而非当前电影的评论数据。 - 用户字段设计不合理:Comment的user字段为
OneToOneField,限制一个用户只能发一条评论,不符合业务逻辑。
修复方案
1. 修正Comment的user字段类型
将OneToOneField改为ForeignKey,允许用户发表多条评论:
class Comment(models.Model): # 其他字段保持不变 user = models.ForeignKey(CustomUser, on_delete=models.CASCADE) # 其他字段保持不变
2. 同步本地Movie记录(核心修复)
处理请求时先检查本地是否存在对应Movie实例,不存在则创建;创建Comment时关联已存在的Movie实例:
def view_movie_detail(request, movie_id): # 获取或创建本地Movie实例 movie, created = Movie.objects.get_or_create(id=movie_id) if request.method == "POST": user = request.user comment = request.POST.get("comment") stars = request.POST.get("stars") if not request.user.is_authenticated: # 改用匿名用户避免固定ID冲突 user = CustomUser.objects.get_or_create(username='anonymous')[0] # 关联已存在的movie实例,而非直接传movie_id Comment.objects.create( comment=comment, stars=float(stars), # 转换为float类型匹配字段 user=user, movie=movie ) return redirect(f"/movie/{movie_id}/") else: 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") # 使用当前movie实例的关联方法 comments = reversed(movie.get_comments()) average = movie.average_stars() return render(request, "Movie/movie_detail.html", { "data": data.json(), "recommendations": recommendations.json(), "type": "movie", "comments": comments, "average" : average, })
3. 优化Movie模型方法(可选)
用Django聚合函数简化平均值计算,提升性能:
from django.db.models import Avg class Movie(models.Model): def get_comments(self): return self.comment_set.all() # 反向关联更简洁 def average_stars(self): avg = self.comment_set.aggregate(Avg('stars'))['stars__avg'] return avg if avg is not None else 0.0
4. 执行数据库迁移
修改模型后,运行迁移命令更新数据库:
python manage.py makemigrations python manage.py migrate
内容的提问来源于stack exchange,提问作者user18970300
相关产品推荐
相关产品推荐

