I have models, objects from which can be represented as a directed graph:
from django.db import models
from django.contrib.auth.models import User
class Record(models.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length=500)
content = models.TextField()
sequels = models.ManyToManyField('self', blank=True, symmetrical=False, through='Extend', related_name='extending')
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Extend(models.Model):
ancestor = models.ForeignKey(Record, on_delete=models.CASCADE, related_name='+')
descendant = models.ForeignKey(Record, on_delete=models.CASCADE, related_name='+')
class Meta:
constraints = [
models.UniqueConstraint(
name="%(app_label)s_%(class)s_unique_relationships",
fields=["ancestor", "descendant"],
),
models.CheckConstraint(
name="%(app_label)s_%(class)s_prevent_self_extend",
check=~models.Q(ancestor=models.F("descendant")),
),
]
Anytime a new instance of Record is created and it has a relationship with another instance through the Extend model, I would love to calculate author.user.userprofile.income attribute in the User instance. The problem is I don't know how to calculate this attribute for all the ancestors of the newly created Record which follows some rule: if the newly created Record costs 50, the first-level ancestor gets 25 and 25 goes to other ancestors, constanly being divided by 2.
I can write the formula for the first-level ancestor, but what for others?
# In case of some purchase:
class ExtendPurchase(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
items = models.ManyToManyField(Record)
date = models.DateField(auto_now_add=True)
cost = models.PositiveIntegerField()
def calculate_income(self):
for item in self.items.all():
item.author.userprofile.income += self.cost / len(self.items.all()) / 2 # first-level; if one would like to extend more than one Record instance
for ancestor in item.extending.all():
ancestor.author.userprofile.income += self.cost / delimiter # I don't know what delimiter is to be written
self.items.save()
If I understand correctly, you want to loop through the records recursively, and divide by 2 the cost in each loop.
Can this work for you?
class ExtendPurchase(models.Model):
# ...
def calculate_income(self):
def __income_from_record(record, cost):
tmp_income = cost
for rel in Extend.objects.select_related('descendant').filter(ancestor=record):
tmp_income += __income_from_record(rel.descendant, cost/2)
return tmp_income
for record in self.items.all():
income = __income_from_record(record, self.cost)
record.ancestor.author.userprofile.income = income
record.ancestor.author.userprofile.save()