TimB
TimB

Reputation: 5844

Converting Python data structures to JavaScript, including functions

I'm creating charts for my Django app using C3.js. In my Django template, I have a JavaScript snippet like this:

<script>
var chart = c3.generate({{ chart_data|safe }});
</script>

In the view, I set up the chart using a Python data structure, which is converted to JSON before being rendered by the template:

chart = {
    'bindto': '#chart',
    'data': {
        'columns': [
            ['data1', 30, 200, 100, 400, 150, 250],
            ['data2', 50, 20, 10, 40, 15, 25]
        ]
    }
}
return render_to_response('chart.html', {'chart_data': json.dumps(chart)})

That works fine, and I get something like this example.

For more complicated C3 charts, I need to be able to include JavaScript functions in the argument to c3.generate. (For example the onclick value in this donut chart.) But JSON doesn't support representing functions, and adding 'onclick': 'function (d, i) { ... };' to the Python dict just results in a string when JSON encoded. Trying to subclass json.JSONEncoder seems fruitless, as what I need to output isn't valid JSON, it's actual JavaScript.

Is there a neat way of doing this? At the moment I'm doing a kludgy workaround, inserting the JavaScript function code into the result of json.dumps().

Upvotes: 2

Views: 483

Answers (1)

TimB
TimB

Reputation: 5844

On reflection, I think what I'm asking is a case of approaching the problem the wrong way, or using the wrong tools to solve the problem.

The data structure given to c3.generate() contains two things:

  1. configuration for how the data is structured and how the chart should look, and
  2. the actual data to be rendered in the chart.

There's no particular reason I need to have the configuration setup in the Django view code. With that realization, I can move the configuration (including the problematic "onclick" JavaScript function I wanted to add) to the template, and inject the data into a separate variable.

The template would be:

<script>
var columns = {{ chart_columns|safe }};
var chart = c3.generate({
    bindto: '#chart',
    data: {
      columns: columns,
      onclick: function (d, i) { /* ... */ }
    }
});
</script>

And the view code:

chart = [
    ['data1', 30, 200, 100, 400, 150, 250],
    ['data2', 50, 20, 10, 40, 15, 25]
]
return render_to_response('chart.html', {'chart_columns': json.dumps(chart)})

As well as solving my problem, this fits better with Django's Model-View-Template design.

Upvotes: 2

Related Questions