如何在Flask中高效地将模板作为变量使用?——图表模板复用场景的优化方案咨询
Great question—your current setup works, but there are cleaner, more Flask-idiomatic ways to handle reusable chart templates that eliminate nested render_template calls and messy autoescape overrides. Let’s walk through two solid approaches:
1. Use Jinja2 Template Includes (Simplest Fix)
This approach leverages Jinja2’s built-in {% include %} tag to embed your chart template directly into your user-facing page, eliminating the need to nest render_template calls. We’ll also fix the autoescape issue with Jinja’s safe tojson filter.
Step 1: Refactor your chart template (template.html)
Remove the top-level <!DOCTYPE html> since it belongs in your parent page, and replace manual safe filters with tojson (handles JSON serialization safely without disabling autoescape):
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.0.0/dist/chart.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js" integrity="sha512-UXumZrZNiOwnTcZSHLOfcTs0aos2MzBWHXOHOuB0J/R44QB0dwY5JgfbvljXcklVf65Gc4El6RjZ+lnwd2az2g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/1.2.1/chartjs-plugin-zoom.min.js" integrity="sha512-klQv6lz2YR+MecyFYMFRuU2eAl8IPRo6zHnsc9n142TJuJHS8CG0ix4Oq9na9ceeg1u5EkBfZsFcV3U7J51iew==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <div class="chart-container" style=""><canvas id="myChart-1"></canvas></div> <div>div-test</div> <script> const dataset_1 = [{ label: "Spread of ", type: 'line', backgroundColor: "blue", order : 3, pointRadius:0, borderWidth: 1, borderColor:"blue", data: {{ data|tojson }}, yAxisID: 'y' }, ]; const data_1 = { labels: {{ label|tojson }}, datasets : dataset_1 }; const zoomOptions = { animations: false, tranistions: false, pan: { enabled: true, mode: 'x', }, zoom: { wheel: { enabled: true, }, drag: { enabled: true, modifierKey: 'ctrl', }, mode: 'x', onZoomComplete({chart}) { chart.update('none'); } } }; const config_1 = { data: data_1, options: { animations:false, transitions:false, responsive: true, interaction: {}, stacked: false, plugins: { zoom : zoomOptions, title: { display: true, text: "bla bla" } }, scales: { y: { type: 'linear', display: true, position: 'left', borderWidth : 0.001, }, x: { display: true, grid :{ display:false }, }, }, }, }; const myChart_1 = new Chart(document.getElementById('myChart-1'),config_1); </script>
Step 2: Update your user-facing page (tender.html)
Use {% include %} to inject the chart template directly:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> {% include 'template.html' %} </body> </html>
Step 3: Simplify your Flask route
Now you only need one render_template call to pass data to both templates:
@app.route('/test') def test(): return render_template('tender.html', data=[2,4,1,9,5,2], label=[0,1,2,3,4,5])
2. Use Jinja2 Macros (More Flexible)
If you want to reuse the chart with different configurations (e.g., custom titles, colors, or chart IDs), Jinja macros are perfect. They let you create reusable "functions" for your templates.
Step 1: Create a chart macro (macros/chart_macros.html)
{% macro line_chart(chart_id, title, data, labels) %} <script src="https://cdn.jsdelivr.net/npm/chart.js@3.0.0/dist/chart.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js" integrity="sha512-UXumZrZNiOwnTcZSHLOfcTs0aos2MzBWHXOHOuB0J/R44QB0dwY5JgfbvljXcklVf65Gc4El6RjZ+lnwd2az2g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/1.2.1/chartjs-plugin-zoom.min.js" integrity="sha512-klQv6lz2YR+MecyFYMFRuU2eAl8IPRo6zHnsc9n142TJuJHS8CG0ix4Oq9na9ceeg1u5EkBfZsFcV3U7J51iew==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <div class="chart-container"><canvas id="{{ chart_id }}"></canvas></div> <script> const dataset = [{ label: "Spread of ", type: 'line', backgroundColor: "blue", order : 3, pointRadius:0, borderWidth: 1, borderColor:"blue", data: {{ data|tojson }}, yAxisID: 'y' }]; const chartData = { labels: {{ labels|tojson }}, datasets : dataset }; const zoomOptions = { animations: false, tranistions: false, pan: { enabled: true, mode: 'x', }, zoom: { wheel: { enabled: true, }, drag: { enabled: true, modifierKey: 'ctrl', }, mode: 'x', onZoomComplete({chart}) { chart.update('none'); } } }; const config = { data: chartData, options: { animations:false, transitions:false, responsive: true, interaction: {}, stacked: false, plugins: { zoom : zoomOptions, title: { display: true, text: "{{ title }}" } }, scales: { y: { type: 'linear', display: true, position: 'left', borderWidth : 0.001, }, x: { display: true, grid :{ display:false }, }, }, }, }; new Chart(document.getElementById('{{ chart_id }}'), config); </script> {% endmacro %}
Step 2: Use the macro in your user-facing page (tender.html)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> {% import 'macros/chart_macros.html' as chart_macros %} </head> <body> {{ chart_macros.line_chart('myChart-1', 'bla bla', data, label) }} </body> </html>
Step 3: Route remains the same
@app.route('/test') def test(): return render_template('tender.html', data=[2,4,1,9,5,2], label=[0,1,2,3,4,5])
Bonus Optimization Tips
- Move dependencies to a base template: Avoid reloading Chart.js scripts on every included chart by adding them to a shared
base.htmltemplate that all your pages extend. - Add unique chart IDs: If you plan to render multiple charts on one page, modify the macro to accept a unique ID parameter to prevent canvas conflicts.
- Extract shared config: Pull common options (like
zoomOptions) into a separate JS file or a macro variable to keep your code DRY.
内容的提问来源于stack exchange,提问作者Strauss Alexandre

