4
\$\begingroup\$

I want to build a simple expense tracking app in Django. Each user can create categories, and each category can have multiple expenses:

enter image description here

Additionally, I wanted to have a value field that shows the total value of the category per month. For this I am using the def save() function in the Expenses class and a def calc_category_value() function called from the signals. I was wondering if this approach is correct:

def calc_category_value(instance):
    today = date.today()
    days = calendar.monthrange(today.year,today.month)[1]
    range_start = "%s-%s-%s" % (today.year,today.month,1)
    range_end = "%s-%s-%s" % (today.year,today.month,days)
    category = Category.objects.get(id=instance.category.id)
    expenses = Expense.objects.filter(date__range=[range_start,range_end],category=category)
    total = 0
    for e in expenses:
        total += e.amount   
    category.value = total
    category.save()

class Profile(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    create_date = models.DateTimeField('date added', auto_now_add=True)
    modify_date = models.DateTimeField('date modified', default=timezone.now)

class Category(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user = models.ForeignKey(Profile, on_delete=models.CASCADE)
    name = models.CharField(max_length=200, unique=True)
    value = models.DecimalField(max_digits=11,decimal_places=2, default=0)
    create_date = models.DateTimeField('date added', auto_now_add=True)
    modify_date = models.DateTimeField('date modified', default=timezone.now)

    class Meta:
        verbose_name_plural = "Categories"

    def __unicode__(self):
        return self.name +' (' + str(self.value) + ')'

class Expense(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=200, blank=True)
    amount = models.DecimalField(max_digits=11, decimal_places=2,default=0)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    date = models.DateField(default=timezone.now)
    create_date = models.DateTimeField('date added', auto_now_add=True)
    modify_date = models.DateTimeField('date modified', default=timezone.now)


    def save(self, *args, **kwargs):
        super(Expense, self).save(*args, **kwargs)
        today = date.today()
        days = calendar.monthrange(today.year,today.month)[1]
        range_start = "%s-%s-%s" % (today.year,today.month,1)
        range_end = "%s-%s-%s" % (today.year,today.month,days)
        expenses = Expense.objects.filter(date__range=[range_start,range_end],category=self.category)
        total = 0
        for e in expenses:
            total += e.amount
        self.category.value = total
        self.category.save()

    class Meta:
        verbose_name_plural = "Expenses"
        order_with_respect_to = 'category'

    def __unicode__(self):
        return self.title +', ' + str(self.amount) +' (' + self.category.name + ')'

Signals.py:

@receiver(post_delete, sender=Expense)
def post_delete(sender, instance, **kwargs):
    calc_category_value(instance)
\$\endgroup\$
2
\$\begingroup\$

You would probably want to show total expenses in the category for each month, not just the last. So Category doesn't have a value. It has a value (which is a bad naming by the way) for each month. This can be modelled simply as a query like (pseudo sql) select sum(amount) from expenses where group = 'some group' group by date.month

And by the way you can use this declarative style here.

total = 0
for e in expenses:
    total += e.amount   
category.value = total

category.value = sum(e.amount for e in expenses)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.