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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 22:45:05