Самореферентные отношения или разные модели (реферальная система Django)

Первый пример реализует самореферентные отношения:

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)

Во втором варианте используется промежуточная модель:

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)

Вопрос в том, что у одного реферала может быть один реферер. Но у реферала может быть много рефералов.

Какой подход будет лучше, если мне нужно использовать анотацию для расчета общего бонуса и количества рефералов. И какой вариант будет оптимальным по производительности?

Нет смысла использовать промежуточную модель: если у каждого User есть (максимум) один реферал, вы только усложните ситуацию, если добавите дополнительную модель: потребуются дополнительные запросы, чтобы получить (id) реферала, и, кроме того, для расчета бонуса потребуется перечисление элементов.

Вы можете ссылаться на self, как вы уже делали в первом примере:

class User(models.Model):
    username = models.CharField(max_length=50, null=True)
    referrer = models.ForeignKey(
        'self', related_name='referrals', null=True, blank=True
    )
    # no referrer_bonus/referral_bonus

Однако вам не нужно хранить referrer_bonus или referral_bonus в явном виде. Вы можете просто вычислить их при необходимости, сделав аннотацию, например:

from django.db.models import Count

User.objects.annotate(referral_bonus=Count('referrals') * bonus_per_referral).get(
    id=my_id
)

Это предотвращает рассинхронизацию referral_bonus в конечном итоге.

Вернуться на верх