Ron Raney
Ron Raney

Reputation: 380

Template variables based on calculations using fields from Django Model

I am using Django 1.10.7 and Python 3.6.1. First my code, then my questions.

My model looks like this:

class Thing(models.Model):
    user = models.ForeignKey('auth.User')
    title = models.CharField(max_length=200)
    start_date = models.DateTimeField(
        default=timezone.now)
    cost_per_day = models.DecimalField(max_digits=6, decimal_places=2)

Here are my views:

def something_list(request):
    things =Thing.objects.filter(start_date__lte=timezone.now()).order_by('start_date')
    return render(request, 'myApp/something_list.html', {'things': things})

def thing_detail(request, pk):
    thing = get_object_or_404(Thing, pk=pk)
    return render(request, 'myApp/thing_detail.html', {'thing': thing})

I have two template blocks for each of these views, where I am using template tags to display variables. For example:

<h2 class="title"><a href="{% url 'thing_detail' pk=thing.pk %}">{{ thing.title }}</a></h2>
<p>User: {{ thing.user }}</p>
<div class="date">
    <p>Start Date: {{ thing.start_date }}</p>
</div>
<p>Cost per day: ${{ thing.cost_per_day }}</p>

So what is happening here?

Web users can enter what I am calling any number of "Things" with 4 fields: User, Title, Start Date, and Cost per Day. I have TWO calculations that I would like to make, and render them in the template.

Problem 1) The user needs to see how many days have elapsed since they originally entered a Start Date. The calculation will be a subtraction of the current time/date (Now) and the Start Date. I was able to accomplish this by using the Django timesince feature shown here:

<button type="button" class="btn btn-info">{{ thing.quit_date|timesince }}</button>

This renders how many days and hours have elapsed - lets call it 'time elapsed' - perfectly. The problem lies in my next calculation.

Problem 2) I need to display a calculation of 'time elapsed' (shown above) multiplied by the Cost per Day variable in the current Model instance. Just for clarity, let's say the start date was 30.5 days ago, and the cost per day is $5.00. I need to multiply 30.5 by $5.00. I would love to simply multiply template tags but I understand that's not how it works. For example:

{{ thing.quit_date|timesince }} * {{ thing.cost_per_day }}

Is there a way to capture the result of this timesince calculation... {{ thing.quit_date|timesince }}... as a variable? I may not be able to use the timesince feature for this calculation.

Once again, what I ultimately need to do here is to multiply the "time elapsed" by the cost per day, like this: (Time Now - Start Date) * (Cost Per Day).

I don't know how to do this in models.py or views.py. I'm looking for the best practice way to do this using Django and Python 3.6. I'm a newbie and I have searched all day for an answer to my particular problem.

Thanks in advance and please let me know if I need to provide more information!

UPDATE
Here is my updated model based on suggestions, albeit not working (the timesince property needs work):

from django.db import models
from django.utils import timezone
from datetime import datetime

class Thing(models.Model):
quitter = models.ForeignKey('auth.User')
title = models.CharField(max_length=200)
quit_date = models.DateTimeField(default=timezone.now)
cost_per_day = models.DecimalField(max_digits=6, decimal_places=2)
notes = models.TextField()

@property
def timesince(self):
    "Time since quit date"
    now = datetime.now() 
    then = self.quit_date
    return (now - then)

@property
def savings(self):
    "Savings since quitting"
    return self.timesince * self.cost_per_day

Here is my updated template:

<h4>Here are some calculations for this information...</h4>
    <p>Days since you quit:</p>
    <button type="button" class="btn btn-info">{{ thing.timesince }}</button>
    <hr>
    <p>How much money you have saved:</p>
    <button type="button" class="btn btn-info">{{ thing.savings }}</button>

I'm certain the problem lies in the subtraction of the dates. Any insight into that would be very helpful. The template variables {{ }} are working, but there is a missing piece in the model.

By the way, when I concatenated "days" to any datetime variable, it game me errors saying .days it gave me errors. Perhaps there is an issue using both a DateTimeField and a datetime.now() instance?

Upvotes: 1

Views: 833

Answers (3)

Ron Raney
Ron Raney

Reputation: 380

Here is an answer that works. I think the problem was that I was using "datetime.now()" instead of "timezone.now()". This might not be the best way of approaching this in Django/Python. I'd appreciate comments on how to make it "best practice". I really appreciate the assistance on this one!

@property
def timesince(self):
    "Time since quit date"
    now = timezone.now() 
    then = self.quit_date
    return  (now - then).days

@property
def savings(self):
    "Savings since quitting"
    return self.timesince * self.cost_per_day

Upvotes: 0

Ivan
Ivan

Reputation: 1507

This really isn't what template tags are for, at all. They're meant to display variables and call it a day, with some minor functional utility bolted on.

For your equations, best practice would be to implement them in the model.

from datetime import datetime

class Thing(models.Model):
    quit_date = models.DateTimeField()
    cost_per_day = models.FloatField() ???

    @property
    def timesince(self):
        # Time since last whatever
        elapsed = datetime.now() - self.quit_date
        return elapsed.days

    @property
    def calculate_cost(self):
        return self.timesince * self.cost_per_day

Then you can just display each of the values using {{ thing.timesince }} and {{ thing.calculate_cost }} in the template.

Upvotes: 1

Brobin
Brobin

Reputation: 3326

I would recommend creating a property on the model itself to calculate this. Subtracting the quit date from the current date will give you a timedelta object. From there we can get the days and take it times the cost.

from django.utils import timezone

class Thing(models.Model):
    ...

    @property
    def cost(self):
        days = (timezone.now().date() - self.quit_date).days
        return days * self.cost_per_day

Then your template becomes very simple.

{{ thing.cost }}

Upvotes: 2

Related Questions