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

基于Django实现小型社交网络消息点赞点踩按钮的最优方案探讨

嘿,这个问题我之前在做类似小型社交项目的时候也纠结过,结合你提到的「面向少量用户」的场景,咱们来仔细拆解下两种方案的优劣,再给你一个明确的建议。

方案对比:新建Like模型 vs 扩展User模型

方案一:新建独立的Like模型

先贴你给出的代码:

class Like(models.Model):
    user = models.ForeignKey(User)
    message = models.ForeignKey(Message)
    created = models.DateTimeField(auto_now_add=True)

优点

  • 数据结构清晰规范:完全符合数据库设计的第三范式,每条记录对应唯一的「用户-消息」互动关系,没有冗余数据,后续维护起来一目了然。
  • 扩展性拉满:如果之后要加「点踩」功能,要么直接复制这个模型改名为Dislike,要么给Like加一个type字段(比如choices=[('LIKE', '赞'), ('DISLIKE', '踩')])就行。甚至你已经加的created字段,以后做「用户近期点赞时间线」「消息热度随时间变化统计」这类功能都能用得上。
  • 不污染核心模型:User模型只需要专注存储用户的核心属性(账号、昵称、头像这些),不会因为各种互动功能变得臃肿,符合单一职责原则。
  • 查询灵活便捷:统计某条消息的点赞数直接Like.objects.filter(message=target_msg).count();查某个用户给哪些消息点过赞,Like.objects.filter(user=current_user).order_by('-created')就能按时间倒序拿到,后续加任何筛选条件都很方便。

缺点

  • 多了一张表:查询消息的时候如果要同时获取点赞数,需要多一次关联查询,但对于「少量用户」的场景,这点性能影响完全可以忽略,几乎感受不到。
  • 代码量略增:需要额外写Like模型的序列化器(如果用DRF的话)、点赞/取消点赞的视图逻辑,但这些都是标准的CRUD操作,复杂度很低,复制粘贴改一改就能用。

方案二:扩展User模型

通常这种方案是给User加多对多字段,比如:

class User(models.Model):
    # 其他核心字段...
    liked_messages = models.ManyToManyField(Message, related_name='liked_by')
    # 如果要加点踩,再加个disliked_messages字段

优点

  • 代码更简洁:不用额外建模型,直接用Django内置的多对多关系封装,查询用户点赞的消息直接user.liked_messages.all(),查询消息的点赞用户msg.liked_by.all(),上手快,代码量少。
  • 适合极简场景:如果你的需求永远只会停留在「点赞/取消点赞」,不需要任何附加属性(比如时间、类型),这种方式用起来确实顺手。

缺点

  • 扩展性极差:一旦需求迭代,比如要记录点赞时间、区分赞和踩,甚至加用户点赞后的附加备注,这种多对多关系就完全搞不定了——因为它只能存关联,不能存额外属性。
  • 污染User模型:如果以后还要加收藏、关注、标记已读这类功能,所有关系都堆在User里,会让这个模型变得越来越臃肿,后续维护起来非常头疼。
  • 查询局限性大:比如想统计某条消息「近7天的点赞数」,因为没有存储时间字段,根本没办法直接实现,只能绕弯路做额外处理。
最终建议:优先选方案一

结合你的场景(小型社交网络,虽然用户少,但需求大概率会迭代),方案一绝对是更优的选择:

  1. 现在你已经加了created字段,这其实已经为后续功能埋下了伏笔,扩展性完全能覆盖未来的需求变化。
  2. 少量用户的情况下,多一张表的性能影响可以忽略,换来的是清晰的数据结构和长期的可维护性。
  3. 就算你觉得方案一的代码量有点多,也可以用Django的through参数做折中——把多对多关系和Like模型结合起来,既保留多对多的便捷查询,又能存储额外字段:
class User(models.Model):
    # 其他核心字段...
    liked_messages = models.ManyToManyField(Message, through='Like', related_name='liked_by')

class Like(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    message = models.ForeignKey(Message, on_delete=models.CASCADE)
    created = models.DateTimeField(auto_now_add=True)
    type = models.CharField(max_length=10, choices=[('LIKE', '赞'), ('DISLIKE', '踩')])

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:52:52