Math on Django Templates

Here's another question about Django.

I have this code:

views.py

cursor = connections['cdr'].cursor()
calls = cursor.execute("SELECT * FROM cdr where calldate > '%s'" %(start_date))
result = [SQLRow(cursor, r) for r in cursor.fetchall()]
return render_to_response("cdr_user.html",
    {'calls':result }, context_instance=RequestContext(request))

I use a MySQL query like that because the database is not part of a django project.

My cdr table has a field called duration, I need to divide that by 60 and multiply the result by a float number like 0.16.

Is there a way to multiply this values using the template tags? If not, is there a good way to do it in my views?

My template is like this:

{% for call in calls %}
    <tr class="{% cycle 'odd' 'even' %}"><h3>
        <td valign="middle" align="center"><h3>{{ call.calldate }}</h3></td>
        <td valign="middle" align="center"><h3>{{ call.disposition }}</h3></td>
        <td valign="middle" align="center"><h3>{{ call.dst }}</h3></td>
        <td valign="middle" align="center"><h3>{{ call.billsec }}</h3></td>
        <td valign="middle" align="center">{{ (call.billsec/60)*0.16 }}</td></h3>
    </tr>
{% endfor %}

The last is where I need to show the value, I know the "(call.billsec/60)*0.16" is impossible to be done there. I wrote it just to represent what I need to show.

Upvotes: 2

Views: 11891

Answers (3)

Pratik Mandrekar
Pratik Mandrekar

Reputation: 9568

If the math operations are not too complex I normally use custom template tags. Add operation is already available as a template tag and I use the below snippet in my project for multiplication, subtraction and division respectively. Put this code inside a .py file inside your app/templatetags location and also add a __init__.py in there.

from django import template

#Django template custom math filters
#Ref : https://code.djangoproject.com/ticket/361
register = template.Library()

def mult(value, arg):
    "Multiplies the arg and the value"
    return int(value) * int(arg)

def sub(value, arg):
    "Subtracts the arg from the value"
    return int(value) - int(arg)

def div(value, arg):
    "Divides the value by the arg"
    return int(value) / int(arg)

register.filter('mult', mult)
register.filter('sub', sub)
register.filter('div', div)

Upvotes: 3

Lie Ryan
Lie Ryan

Reputation: 64837

EDIT: the following answer is totally wrong, since I thought OP was using sqlite. MySQL has its own way of wrapping things into dictionary, see far below.

You can subclass sqlite3.Row and write your own "computed field":

class MyRow(sqlite3.Row):
    def comp_billsec(self):
        return (self['billsec'] / 60) * 0.16

cursor = ...
cursor.row_factory = MyRow
for r in cursor.execute('...'):
    print r['billsec'], r.comp_billsec()

note that our comp_billsec is accessed using method call syntax, while sqlite3.Row factory provides access through dictionary syntax. This discrepancy would disappear in django template since inside django template, dictionary access and zero-argument function call have the same syntax, so you can do {{ call.billsec }} and {{ call.comp_billsec }}.

EDIT: In MySQL, you can insert computed values in the view along the lines of:

cursor = connections['cdr'].cursor(cursorclass=MySQLdb.cursors.DictCursor)
calls = cursor.execute("...")
result = [r + dict(comp_billsec=r['billsec'] / 60 * 0.16) for r in cursor.fetchall()]
return render_to_response("cdr_user.html",
    {'calls':result }, context_instance=RequestContext(request))

Additionaly, you should use parameterized query (note the comma instead of %):

cursor.execute("SELECT * FROM cdr where calldate > '%s'", (start_date,))

Your previous code is subject to SQL injection security issue since you're interpolating start_date into the SQL query directly. If start_date contains ' OR 1 OR ', for example, your query will be interpolated as SELECT * FROM cdr where calldate > '' OR 1 OR '' which will select all rows in the table; it could be even worse.

Upvotes: 1

Soravux
Soravux

Reputation: 9963

You can do it on three different layers:

  • Database level. SQL is a powerful language capable of mathematics. You could write your equation in the select part of your query. In your case, that should be along the lines SELECT (duration/60*0.16) FROM cdr;. Examples can be found here and on Google. Note that in this case, stress (algorithm complexity) is put on the MySQL server process and not the Python process.
  • View level. In your example, just before your return, you could loop over every element of your result variable to modify its value. You can follow the example that was given by Lie Ryan for this level.
  • Template level. This is done by a custom filter. You can write your custom filter as written in the documentation and pipe your template variable through this filter in order to get the desired value.

Something along these lines would represent a custom filter applicable on your template(s):

@register.filter
def customFilter(value):
    return value / 60.0 * 0.16

You would then use it this way in your template, after {% load %}-ing the custom filter (read the documentation for more implementation information):

{{ billsec|customFilter }}

Upvotes: 4

Related Questions