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)
方案优势
- 完美匹配需求:通过自关联外键
referrer直接定义推荐关系,related_name='referrals'可快速反向获取某个用户推荐的所有人;null=True, blank=True支持无推荐人的用户,天然满足“一推多、被推一”的关系约束。 - 性能更优:统计操作仅需对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) )
- 统计每个用户的推荐人数:
- 模型简洁:无需额外维护中间表,数据结构更直观,降低代码复杂度。
方案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)
方案特点
- 满足需求但冗余:通过
OneToOneField保证一个被推荐人只能对应一条推荐记录,实现关系约束,但多了一张额外的中间表,增加了数据存储和查询的复杂度。 - 性能劣势:所有统计操作都需要关联User和Referral两张表,比如统计推荐人数会生成JOIN查询,数据量越大,跨表关联的性能损耗越明显。
- 仅适合扩展场景:如果未来需要给推荐关系添加更多属性(比如推荐时间、审核状态、额外规则字段),中间模型会更灵活,但当前需求下完全没必要增加这个复杂度。
最终结论
方案1(自关联User模型)是当前需求下的最优选择,既完美匹配关系约束,又在注解统计时拥有更高的性能,同时模型结构简洁易维护。
内容的提问来源于stack exchange,提问作者darl1ne
相关产品推荐
相关产品推荐

