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

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('/')
错误根源
  1. 数据库表结构未同步:虽然你删除了LikePost模型里的num_interactions_total字段和相关迁移文件,但PostgreSQL数据库中的core_likepost表仍保留该字段,且该字段被设置为NOT NULL约束。而core_dislikepost表可能已经完成了字段删除(或从未存在过该字段),所以点踩操作不受影响。
  2. 迁移操作不规范:直接删除迁移文件是错误的做法,Django依赖迁移文件跟踪模型与数据库的同步状态。删除迁移文件后,Django无法生成正确的迁移语句来修改数据库表结构,导致旧字段残留。
解决方法

方法一:通过Django迁移修复

  1. 确保LikePost模型中确实没有num_interactions_total字段,然后运行命令生成新的迁移文件:
    python manage.py makemigrations
    
  2. 执行迁移,同步数据库结构:
    python manage.py migrate
    

方法二:手动修改数据库(仅测试环境使用)

如果迁移方法无效,可直接连接PostgreSQL数据库,执行SQL语句删除残留字段:

ALTER TABLE core_likepost DROP COLUMN num_interactions_total;

后续规范

之后修改模型时,禁止直接删除迁移文件,必须通过makemigrations生成新迁移,再用migrate同步数据库,确保模型与数据库结构完全一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 11:21:03