Django Like按钮触发IntegrityError:违反非空约束求助
问题描述
开发的社交应用中,点赞与点踩操作的统计逻辑完全一致,但点赞按钮触发了空值错误,点踩按钮却能正常工作。报错信息如下:
IntegrityError at /like-post
null value in column "num_interactions_total" of relation "core_likepost" violates not-null constraint
DETAIL: Failing row contains (98, a4aaa95b-a3a5-49a6-8f63-de768bfa0786, 2023-02-02 18:01:13.121023+00, , 1, null, 7).
已从LikePost模型和视图代码中删除num_interactions_total字段,也删除了相关迁移文件,但仍出现该错误,使用PostgreSQL数据库。疑惑为何仅点赞操作报错,该如何解决这个空值问题?相关模型与视图代码如下:
def like_pdf(instance, filename): ext = filename.split('.')[-1] filename = 'post{}.{}'.format(instance.interactions_total, ext) return os.path.join('uploads', filename) class LikePost(models.Model): post_id = models.CharField(max_length=500) created_at = models.DateTimeField(default=datetime.now) num_likes_total = models.IntegerField(default=0) interactions_total = models.IntegerField(default=0) pdf_like = models.FileField(upload_to=like_pdf, default=None, null=True) def __str__(self): return self.post_id def dislike_pdf(instance, filename): ext = filename.split('.')[-1] filename = 'post{}.{}'.format(instance.interactions_total, ext) return os.path.join('uploads', filename) class DislikePost(models.Model): post_id = models.CharField(max_length=500) created_at = models.DateTimeField(default=datetime.now) num_dislikes_total = models.IntegerField(default=0) interactions_total = models.IntegerField(default=0) pdf_dislike = models.FileField(upload_to=dislike_pdf, default=None, null=True) def __str__(self): return self.post_id def like_post(request): post_id = request.GET.get('post_id') post = Post.objects.get(id=post_id) post.number_of_likes = post.number_of_likes + 1 num_likes_total = LikePost.objects.count() + 1 interactions_total = InteractionPost.objects.count() new_like = LikePost.objects.create(post_id=post_id, num_likes_total=num_likes_total, interactions_total=interactions_total) new_like.save() new_interaction = InteractionPost.objects.create(post_id=post_id) new_interaction.save() post.interaction_count = post.interaction_count + 1 likes_string = "" word = "like " i = 0 while i < 1: post.likes_string = post.likes_string + word + os.linesep i += 1 post.save() # create pdf dislike buffer = io.BytesIO() countobject = UserCount.objects.first() p = canvas.Canvas(buffer, pagesize=A4) x_start = 50 y_start = 200 x = 100 y = 275 p.roundRect(x_start, y_start, 500, 400, 10, stroke=1, fill=0) p.roundRect(x, y, 400, 300, 10, stroke=1, fill=0) p.drawString(100, 700, "new_interaction: like_post // user no.:") p.drawString(100, 675, str(new_like.created_at)) p.drawString(200, 650, str(post.num_post)) p.drawString(100, 650, "post no.:") p.drawString(400, 700, str(countobject)) p1 = Paragraph(post.dislikes_string) p1.wrapOn(p, 400, 1000) p1.drawOn(p, 200, 700) p2 = Paragraph(post.likes_string) p2.wrapOn(p, 400, 1000) p2.drawOn(p, 0, 800) p.save() buffer.seek(0) new_like.pdf_like.save(f"post{interactions_total}.pdf", buffer) new_like.save() return redirect('/') def dislike_post(request): post_id = request.GET.get('post_id') post = Post.objects.get(id=post_id) post.number_of_dislikes = post.number_of_dislikes + 1 num_dislikes_total = DislikePost.objects.count() + 1 interactions_total = InteractionPost.objects.count() new_dislike = DislikePost.objects.create(post_id=post_id, num_dislikes_total=num_dislikes_total, interactions_total=interactions_total) new_dislike.save() new_interaction = InteractionPost.objects.create(post_id=post_id) new_interaction.save() post.interaction_count = post.interaction_count + 1 dislikes_string = "" word = "dislike " i = 0 while i < 1: post.dislikes_string = post.dislikes_string + word + os.linesep i += 1 post.save() #create pdf dislike buffer = io.BytesIO() countobject = UserCount.objects.first() p = canvas.Canvas(buffer, pagesize=A4, ) x_start = 50 y_start = 200 x = 100 y = 275 p.roundRect(x_start, y_start, 500, 400, 10, stroke=1, fill=0) p.roundRect(x, y, 400, 300, 10, stroke=1, fill=0) p.drawString(100, 700, "new_interaction: dislike_post // user no.:") p.drawString(100, 675, str(new_dislike.created_at)) p.drawString(200, 650, str(post.num_post)) p.drawString(100, 650, "post no.:") p.drawString(400, 700, str(countobject)) p1 = Paragraph(post.dislikes_string) p1.wrapOn(p, 400, 1000) p1.drawOn(p, 0, 300) p2 = Paragraph(post.likes_string) p2.wrapOn(p, 400, 1000) p2.drawOn(p, 110, 600) p.save() buffer.seek(0) new_dislike.pdf_dislike.save(f"post{interactions_total}.pdf", buffer) new_dislike.save() return redirect('/')
错误根源
- 数据库表结构未同步:虽然你删除了
LikePost模型里的num_interactions_total字段和相关迁移文件,但PostgreSQL数据库中的core_likepost表仍保留该字段,且该字段被设置为NOT NULL约束。而core_dislikepost表可能已经完成了字段删除(或从未存在过该字段),所以点踩操作不受影响。 - 迁移操作不规范:直接删除迁移文件是错误的做法,Django依赖迁移文件跟踪模型与数据库的同步状态。删除迁移文件后,Django无法生成正确的迁移语句来修改数据库表结构,导致旧字段残留。
解决方法
方法一:通过Django迁移修复
- 确保
LikePost模型中确实没有num_interactions_total字段,然后运行命令生成新的迁移文件:python manage.py makemigrations - 执行迁移,同步数据库结构:
python manage.py migrate
方法二:手动修改数据库(仅测试环境使用)
如果迁移方法无效,可直接连接PostgreSQL数据库,执行SQL语句删除残留字段:
ALTER TABLE core_likepost DROP COLUMN num_interactions_total;
后续规范
之后修改模型时,禁止直接删除迁移文件,必须通过makemigrations生成新迁移,再用migrate同步数据库,确保模型与数据库结构完全一致。
内容的提问来源于stack exchange,提问作者amber
相关产品推荐
相关产品推荐

