Passing and Using Dictionaries in Django Template Tags

Question:

I have a function that uses the psutil library to fetch CPU usage data.

import psutil
from django.utils.safestring import mark_safe

def get_cpu():
    """Gets the system-wide CPU utilisation percentage"""
    utilisation = list(psutil.cpu_percent(interval=1, percpu=True))
    cpu_data_utilisation = []
    load_avg_list = []
    cpu_data = {}
    for count in range(len(utilisation)):
        count = count + 1
        cpu_data_utilisation.append({
            "label" : str("CPU " + str(count)),
            "util" : round(utilisation[count-1])
        })
    load_avg_tuple = tuple(psutil.getloadavg())
    load_avg_1 = load_avg_tuple[0]
    load_avg_5 = load_avg_tuple[1]
    load_avg_15 = load_avg_tuple[2]
    load_avg_list.append({
        "1"  : round(load_avg_1),
        "5"  : round(load_avg_5),
        "15" : round(load_avg_15)
    })
    cpu_data = {
        "core_util" : cpu_data_utilisation,
        "load_avg"  : load_avg_list,
    }
    return cpu_data

I also have a template tag which calls and returns the get_cpu function.

@register.simple_tag
def get_cpu_data():
    return mark_safe(get_cpu())

In the HTML file, adding these two lines:

{% get_cpu_data as cpu_data %}
{{ cpu_data }}

Renders this:

{'core_util': [{'label': 'CPU 1', 'util': 66}, {'label': 'CPU 2', 'util': 21}, {'label': 'CPU 3', 'util': 62}, {'label': 'CPU 4', 'util': 22}, {'label': 'CPU 5', 'util': 56}, {'label': 'CPU 6', 'util': 22}, {'label': 'CPU 7', 'util': 54}, {'label': 'CPU 8', 'util': 23}], 'load_avg': [{'1': 5, '5': 5, '15': 4}]}

The dictionary has two keys, core_util and load_avg. I want to take the lists out of the values paired to the keys and iterate.

The template code below renders nothing.

{% for key, val in cpu_data.items %} 
    {{ key }}
{% endfor %}

But, this template code:

{% for data in cpu_data %} 
    {{ data }}
{% endfor %}

Returns this:

    {
 
    '
 
    c
 
    o
 
    r
 
    e
 
    _
 
    u
 
    t
 
    i
 
    l
 
    '
 
    :
 
     
 
    [
 
    {
 
    '
 
    l
 
    a
 
    b
 
    e
 
    l
 
    '
 
    :
 
     
 
    '
 
    C
 
    P
 
    U
 
     
 
    1
 
    '
 
    ,
 
     
 
    '
 
    u
 
    t
 
    i
 
    l
 
    '
 
    :
 
     
 
    5
 
    0
 
    }
 
    ,
 
     
 
    {
 
    '
 
    l
 
    a
 
    b
 
    e
 
    l
 
    '
 
    :
 
     
 
    '
 
    C
 
    P
 
    U
 
     
 
    2
 
    '
 
    ,
 
     
 
    '
 
    u
 
    t
 
    i
 
    l
 
    '
 
    :
 
     
 
    1
 
    8
 
    }
 
    ,
 
     
 
    {
 
    '
 
    l
 
    a
 
    b
 
    e
 
    l
 
    '
 
    :
 
     
 
    '
 
    C
 
    P
 
    U
 
     
 
    3
 
    '
 
    ,
 
     
 
    '
 
    u
 
    t
 
    i
 
    l
 
    '
 
    :
 
     
 
    4
 
    3
 
    }
 
    ,
 
     
 
    {
 
    '
 
    l
 
    a
 
    b
 
    e
 
    l
 
    '
 
    :
 
     
 
    '
 
    C
 
    P
 
    U
 
     
 
    4
 
    '
 
    ,
 
     
 
    '
 
    u
 
    t
 
    i
 
    l
 
    '
 
    :
 
     
 
    1
 
    8
 
    }
 
    ,
 
     
 
    {
 
    '
 
    l
 
    a
 
    b
 
    e
 
    l
 
    '
 
    :
 
     
 
    '
 
    C
 
    P
 
    U
 
     
 
    5
 
    '
 
    ,
 
     
 
    '
 
    u
 
    t
 
    i
 
    l
 
    '
 
    :
 
     
 
    3
 
    8
 
    }
 
    ,
 
     
 
    {
 
    '
 
    l
 
    a
 
    b
 
    e
 
    l
 
    '
 
    :
 
     
 
    '
 
    C
 
    P
 
    U
 
     
 
    6
 
    '
 
    ,
 
     
 
    '
 
    u
 
    t
 
    i
 
    l
 
    '
 
    :
 
     
 
    2
 
    0
 
    }
 
    ,
 
     
 
    {
 
    '
 
    l
 
    a
 
    b
 
    e
 
    l
 
    '
 
    :
 
     
 
    '
 
    C
 
    P
 
    U
 
     
 
    7
 
    '
 
    ,
 
     
 
    '
 
    u
 
    t
 
    i
 
    l
 
    '
 
    :
 
     
 
    3
 
    5
 
    }
 
    ,
 
     
 
    {
 
    '
 
    l
 
    a
 
    b
 
    e
 
    l
 
    '
 
    :
 
     
 
    '
 
    C
 
    P
 
    U
 
     
 
    8
 
    '
 
    ,
 
     
 
    '
 
    u
 
    t
 
    i
 
    l
 
    '
 
    :
 
     
 
    1
 
    8
 
    }
 
    ]
 
    ,
 
     
 
    '
 
    l
 
    o
 
    a
 
    d
 
    _
 
    a
 
    v
 
    g
 
    '
 
    :
 
     
 
    [
 
    {
 
    '
 
    1
 
    '
 
    :
 
     
 
    6
 
    ,
 
     
 
    '
 
    5
 
    '
 
    :
 
     
 
    5
 
    ,
 
     
 
    '
 
    1
 
    5
 
    '
 
    :
 
     
 
    4
 
    }
 
    ]
 
    }

An extremely long unformatted string with one character in each line.

What am I doing wrong and how can I fix this?

Asked By: SidS

||

Answers:

In your template tag you have the following line:

return mark_safe(get_cpu())

mark_safe returns a SafeString which is a subclass of str. Meaning what happens here is that your dictionary is converted to a string (actually instance of SafeString) which is marked as safe. Next when you try to loop over it may not actually be safe and hence it is escaped by the template engine. If you actually want to use it as a dictionary don’t call mark_safe on it:

@register.simple_tag
def get_cpu_data():
    return get_cpu()

Your use case might be that the contents of this dictionary is safe, which you can render as below if you want, or you can use mark_safe on the actual values of the dictionary:

{% for key, val in cpu_data.items %} 
    {{ key|safe }}: {{ val|safe }}
{% endfor %}
Answered By: Abdul Aziz Barkat

This is not exactly related to the above problem, but this posts save me after two hours of hell, so I will point it out here for somebody similarly desperate in the future.

If you want to work with registered simple tag in your template, YOU MUST use this line first:

{% your_simple_tag_function_name as name_to_use_in_template %}

so as above specific example

{% get_cpu_data as cpu_data %}

If you don’t do this and you will try to use directly your_simple_tag_function_name for any operation in template like looping etc., it will just not work. There is no mention about it in any Django docs at all!

Answered By: p-a