Django创建GameComment时如何累加GamePreview的integer字段数值
问题原因
你原来的create方法无法生效有两个核心原因:
- 仅在内存中创建了
GameComment和修改了GamePreview的comments值,没有调用save()方法将变更写入数据库 - 新增评论的逻辑不应该只在自定义的
create方法里实现,如果后续有其他入口创建GameComment实例,计数逻辑会被跳过,且方法返回值不符合预期,应该返回评论实例而非预览实例
正确实现方案
下面给出两种常用的实现方式,均内置了并发安全处理,避免多请求同时创建评论时计数不准的问题:
方案1:重写GameComment的save方法
这种方式逻辑最直观,适合绝大多数场景:
from django.db import models from django.db.models import F class Game(models.Model): description = models.TextField(max_length=8192) class GamePreview(models.Model): game = models.OneToOneField(Game, on_delete=models.CASCADE) comments = models.IntegerField(default=0) class GameComment(models.Model): game = models.ForeignKey(Game, on_delete=models.CASCADE) comment = models.CharField(max_length=512) def save(self, *args, **kwargs): # 只有首次创建评论(还未生成主键)时才触发计数+1,更新评论时不重复计数 is_new = self.pk is None super().save(*args, **kwargs) if is_new: # 使用F表达式实现数据库层面原子操作,避免并发冲突 GamePreview.objects.filter(game=self.game).update(comments=F('comments') + 1)
后续创建评论直接调用常规的save逻辑即可:
# 示例创建评论代码 comment = GameComment(game=你的game实例, comment="评论内容") comment.save()
方案2:使用Djangopost_save信号
如果希望和模型本身的逻辑解耦,可以用信号实现,适合项目中信号使用比较规范的场景:
首先在模型文件同级的signals.py中定义信号处理函数,或者直接写在模型文件末尾:
from django.db.models.signals import post_save from django.dispatch import receiver from django.db.models import F @receiver(post_save, sender=GameComment) def update_game_preview_comment_count(sender, instance, created, **kwargs): if created: GamePreview.objects.filter(game=instance.game).update(comments=F('comments') + 1)
然后需要在对应app的apps.py中注册信号,避免不生效:
from django.apps import AppConfig class YourAppConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = '你的app名称' def ready(self): import 你的app名称.signals
补充说明
如果不需要做高频的计数筛选、排序场景,也可以不用冗余存储comments字段,需要统计时直接用ORM的annotate动态计算即可,避免维护计数的额外逻辑。
内容的提问来源于stack exchange,提问作者Lun
相关产品推荐
相关产品推荐

