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

Django推荐系统:自关联与中间模型的注解计算及性能对比

推荐关系模型方案对比:自关联 vs 中间模型

需求背景

一个被推荐人仅能拥有一个推荐人,一个推荐人可拥有多个被推荐人,需通过Django注解(annotation)计算用户的总奖金及推荐人数。


方案1:自关联User模型

代码实现:

class User(models.Model):
    username = models.CharField(max_length=50, null=True)
    referrer = models.ForeignKey("self", related_name='referrals', null=True, blank=True) 
    referrer_bonus = models.IntegerField(default=0) 
    referral_bonus = models.IntegerField(default=0)

方案优势

  1. 完美匹配需求:通过自关联外键referrer直接定义推荐关系,related_name='referrals'可快速反向获取某个用户推荐的所有人;null=True, blank=True支持无推荐人的用户,天然满足“一推多、被推一”的关系约束。
  2. 性能更优:统计操作仅需对User单表进行查询,无需关联额外表:
    • 统计每个用户的推荐人数:User.objects.annotate(referral_count=Count('referrals'))
    • 统计每个用户的总奖金(自身的推荐奖励+推荐他人获得的奖励总和):
      from django.db.models import F, Sum, Coalesce
      User.objects.annotate(
          total_referrer_bonus=Sum('referrals__referrer_bonus')
      ).annotate(
          total_bonus=F('referral_bonus') + Coalesce(F('total_referrer_bonus'), 0)
      )
      
    这类查询只涉及User表的自连接,SQL执行开销远低于跨表关联。
  3. 模型简洁:无需额外维护中间表,数据结构更直观,降低代码复杂度。

方案2:中间模型Referral

代码实现:

class Referral(models.Model):
    referrer = models.ForeignKey(User, on_delete=models.PROTECT, related_name="referrals")
    referral = models.OneToOneField(User, on_delete=models.PROTECT, related_name='referral')
    referrer_bonus = models.IntegerField(default=0) 
    referral_bonus = models.IntegerField(default=0)

方案特点

  1. 满足需求但冗余:通过OneToOneField保证一个被推荐人只能对应一条推荐记录,实现关系约束,但多了一张额外的中间表,增加了数据存储和查询的复杂度。
  2. 性能劣势:所有统计操作都需要关联User和Referral两张表,比如统计推荐人数会生成JOIN查询,数据量越大,跨表关联的性能损耗越明显。
  3. 仅适合扩展场景:如果未来需要给推荐关系添加更多属性(比如推荐时间、审核状态、额外规则字段),中间模型会更灵活,但当前需求下完全没必要增加这个复杂度。

最终结论

方案1(自关联User模型)是当前需求下的最优选择,既完美匹配关系约束,又在注解统计时拥有更高的性能,同时模型结构简洁易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 14:07:14