Django ManyToManyField 限制用户不能同时点赞和点踩评论的实现方法
Django评论模块点赞点踩互斥实现方案
可以从底层到上层多层校验实现需求,避免出现同一个用户同时点赞点踩同一条评论的脏数据:
- 重写模型
clean方法添加校验规则,同时重写save方法自动触发校验,适合直接操作模型实例的场景:
from django.core.exceptions import ValidationError class Comment(models.Model): """docstring for comment.""" # 原有字段不变 likes = models.ManyToManyField("User", related_name = "CommentLikes", blank = True) dislikes = models.ManyToManyField("User", related_name = "CommentDislikes", blank = True) def clean(self): super().clean() # 检查点赞和点踩列表是否存在重叠用户 if self.likes.intersection(self.dislikes.all()).exists(): raise ValidationError("同一条评论下用户不能同时点赞和点踩") def save(self, *args, **kwargs): # 保存前自动执行校验逻辑 self.full_clean() super().save(*args, **kwargs)
- 监听
m2m_changed信号拦截多对多操作,针对直接调用likes.add()/dislikes.add()这类多对多修改的场景做底层拦截,是最稳妥的校验方式:
from django.db.models.signals import m2m_changed from django.dispatch import receiver # 拦截点赞添加操作 @receiver(m2m_changed, sender=Comment.likes.through) def prevent_like_dislike_conflict_when_add_like(sender, instance, action, pk_set, **kwargs): if action == "pre_add": # 检查要添加的用户是否已经点踩 if instance.dislikes.filter(pk__in=pk_set).exists(): raise ValidationError("用户已点踩该评论,无法同时点赞") # 拦截点踩添加操作 @receiver(m2m_changed, sender=Comment.dislikes.through) def prevent_like_dislike_conflict_when_add_dislike(sender, instance, action, pk_set, **kwargs): if action == "pre_add": # 检查要添加的用户是否已经点赞 if instance.likes.filter(pk__in=pk_set).exists(): raise ValidationError("用户已点赞该评论,无法同时点踩")
- 可选优化方案:单独设计反应中间表替代两个多对多字段,通过数据库联合唯一约束从根源避免冲突,后期扩展新反应类型也更灵活:
class Comment(models.Model): """docstring for comment.""" # 原有其他字段保留,删除likes、dislikes两个多对多字段 ... class CommentReaction(models.Model): REACTION_TYPE = ( ("like", "点赞"), ("dislike", "点踩"), ) user = models.ForeignKey("User", on_delete=models.CASCADE, related_name="comment_reactions") comment = models.ForeignKey(Comment, on_delete=models.CASCADE, related_name="reactions") type = models.CharField(max_length=10, choices=REACTION_TYPE) class Meta: constraints = [ # 联合唯一约束,保证一个用户对同一条评论只能有一条反应记录 models.UniqueConstraint(fields=["user", "comment"], name="unique_user_comment_reaction") ]
内容的提问来源于stack exchange,提问作者AIGAMER
相关产品推荐
相关产品推荐

