Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using an aggregate in a Django model's property

Schema Description

enter image description here

A project could have multiple project items, and a project item could have multiple acquisition costs. For example, capital project #1 contains two project items:

  1. Qty. 1 production equipment purchased
    • One acquisition cost of $5,000 for the purchasing the equipment from a supplier
  2. Qty. 1 test fixture constructed by staff
    • Twelve $1,000 purchases each for total acquisition costs of $12,000.

The production equipment might have only one cost associated with purchasing it. However, the test fixture could have multiple costs associated with constructing it.

Desire

I want to be able to determine the total acquisition costs for each item, and I want to be able to determine the total acquisition costs for each project. Based on the above example:

  • Item 1 total acquisition costs = $5,000
  • Item 2 total acquisition costs = $12,000
  • Capital project total acquisition costs = $17,000

Questions

  1. Should the total_acquisition_costs be calculated using Django's aggregates or saved as a DecimalField on the Project and ProjectItem models?

  2. Should I create a total_acquisition_costs property for the ProjectItem and Project models? My gut feel is yes to create a property.

    My gut feel is to calculate the values using aggregates, so as to avoid database denormalization. Specifically, I'm leaning toward using Django's aggregate Sum function to create a total_acquisition_costs property. However, I'm not grokking how to put an aggregate on a model as a property so that it just returns a value instead of a dictionary.

Current Code

Note: My current code does not use Django's aggregate capabilities, but it works.

class ProjectItem(models.Model):
    project = models.ForeignKey(CapExProject)
    name = models.CharField(max_length=128)

    @property
    def total_acquisition_costs(self):
        acquisition_costs = self.acquisitioncost_set.only(
                'amount')
        total_acquisition_costs = 0
        for acquisition_cost in acquisition_costs:
        total_acquisition_costs += acquisition_cost.amount
        return total_acquisition_costs
like image 350
Matthew Rankin Avatar asked Oct 19 '11 19:10

Matthew Rankin


1 Answers

I like your idea of using Sum but making it a property. Something like this should work:

class Project(models.Model):
    @property
    def total_acquisition_costs(self):
        return AcquisitionCost.objects.filter(item__project=self).aggregate(total=Sum("amount"))["total"]

class ProjectItem(models.Model):
    @property
    def total_acquisition_costs(self):
        return self.acquisitioncost_set.aggregate(total=Sum("amount"))["total"]
like image 104
Brian from QuantRocket Avatar answered Oct 20 '22 07:10

Brian from QuantRocket