Lazy functions in Django [Functional programming]

I love to write functions instead of classes. However, Django does not allow us to call functions inside templates.

Sometimes we want to postpone the function calls in templates because they might be expensive. Thus we might want to call them inside the template’s cache fragment or inside an if statement.

My way around them is to use a lazy_cache function to postpone and cache a function’s result.

Thus the whole usage is something like this:

import datetime as dt
from utilities.functional import lazy_cached

def expensive_computation(result_date, page_number=1):
    # do some expensive stuff
    return results

def latest_results(request):
    result_date = dt.datetime.today()
    page_number = request.GET.get('page')
    results = lazy_cached(
        expensive_computation,
        result_date,
        page_number=request.GET,
    )

    return render(
        request,
        "latest-results.html",
        {
            "results": results,
            "page_number": page_number,
        },
    )

In templates we can now do:

{% load cache %}

{% block content %}
{% cache 30000 results_page page_number %}
{% for result in results %}
    <div>{ { result.name }}</div>
{% endfor %}
{% endcache %}

My recipe for lazy_cached function is:

def lazy_cached(func, *args, **kwargs):
    state = {"value": None, "realized?": False}

    def wrap():
        if state["realized?"]:
            return state["value"]
        state["value"] = func(*args, **kwargs)
        state["realized?"] = True
        return state["value"]

    return wrap
Pratyush Mittal @pratyush