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

Django重写模型save方法统计点赞数到total_likes字段报错如何解决

问题修复方案

你代码里存在以下几处错误:

  • 缩进错误:save 方法没有定义在 Question 类的内部,属于全局函数,不会被模型调用
  • 多对多字段调用错误:你给 likes 字段显式设置了 related_name='likes',不需要加 _set 后缀,直接调用 self.likes.count() 即可,likes_set 是不存在的属性,会触发报错
  • 逻辑缺陷:多对多关系的修改(用户点赞/取消赞操作)默认不会触发 Question 模型的 save 方法,即便修正上述两个错误,total_likes 也不会在点赞操作后自动更新
  • 额外小问题:代码中直接使用了 impression_choices 但没有提前定义,也会触发报错

修复方案1:保留total_likes存储字段,用信号同步

适合点赞量大、需要对点赞数字段做索引/过滤的场景:

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import m2m_changed
from django.dispatch import receiver

# 提前定义impression选项,可根据业务需求自行调整
impression_choices = (
    ('positive', '好评'),
    ('neutral', '中立'),
    ('negative', '差评'),
)

class Question(models.Model):
    text = models.TextField()
    voters = models.ManyToManyField(to=User, blank=True, related_name='voted_questions')
    impression = models.CharField(max_length=30, choices=impression_choices, blank=True, null=True)
    date_created = models.DateTimeField(auto_now_add=True)
    # 修改related_name为更合理的命名,避免User侧属性歧义
    likes = models.ManyToManyField(to=User, related_name='liked_questions', blank=True)
    total_likes = models.IntegerField(default=0)

    def __str__(self):
        return self.text

    # 把save方法移到类内部
    def save(self, *args, **kwargs):
        self.total_likes = self.likes.count()
        super().save(*args, **kwargs)

# 新增多对多变更信号,点赞/取消赞时自动更新总点赞数
@receiver(m2m_changed, sender=Question.likes.through)
def update_likes_count(sender, instance, action, **kwargs):
    # 只有在添加、移除、清空点赞操作后才更新
    if action in ["post_add", "post_remove", "post_clear"]:
        instance.total_likes = instance.likes.count()
        instance.save(update_fields=['total_likes'])


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    text = models.CharField(max_length=20)
    votes = models.IntegerField(blank=True, default=0)

    def __str__(self):
        return self.text

修复方案2:轻量场景直接用动态属性,无需存储

如果项目点赞量不大,不需要对点赞数字段做索引/过滤,可以直接删掉total_likes存储字段,用property动态计算点赞数,完全不需要考虑数据同步问题:

class Question(models.Model):
    text = models.TextField()
    voters = models.ManyToManyField(to=User, blank=True, related_name='voted_questions')
    impression = models.CharField(max_length=30, choices=impression_choices, blank=True, null=True)
    date_created = models.DateTimeField(auto_now_add=True)
    likes = models.ManyToManyField(to=User, related_name='liked_questions', blank=True)

    # 新增动态属性,访问时实时计算点赞数
    @property
    def total_likes(self):
        return self.likes.count()

    def __str__(self):
        return self.text

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 11:09:02