Extracting data from jinja for a dashboard

Published: (March 26, 2026 at 09:22 AM EDT)
5 min read
Source: Dev.to

Source: Dev.to

Understanding Data Flow in a Flask‑Jinja Application

A key feature of modern web applications is the ability to retrieve data from a backend system and display it dynamically on a webpage. In a Flask application, this process involves interaction between three main components:

  1. Database – e.g., MySQL, where the data lives.
  2. Flask backend – extracts data and passes it to the template.
  3. Jinja template engine – renders the data into HTML.

1. Retrieving Data in Flask

When a user accesses a specific route, Flask executes the associated view function. Inside that function a database query is performed, e.g.:

@app.route('/products')
def products():
    # Query the database for all product records
    product_rows = db.session.execute(
        text("SELECT id, name, price, description FROM products")
    ).fetchall()
    # Convert rows to a list of dictionaries (optional but convenient)
    products = [dict(row) for row in product_rows]
    return render_template('products.html', products=products)
  • The query transforms stored rows into a Python‑friendly format (list, tuple, dict).
  • This list is then supplied to the template as a variable (products).

2. Passing Variables to the Template

Flask’s render_template lets you inject any number of variables into a Jinja template:

return render_template('dashboard.html',
                       user=current_user,
                       orders=order_list,
                       stats=summary_stats)

Each variable receives a name that the template will use to reference the data, creating a clear bridge between backend and frontend.


3. Rendering with Jinja

Jinja processes the variables and produces HTML. Common patterns include:

a. Loops


{% for product in products %}
  - {{ product.name }} – ${{ product.price }}

{% endfor %}

Loops let you display collections without repetitive markup.

b. Conditionals

{% if user.is_authenticated %}
  
Welcome, {{ user.name }}!

{% else %}
  [Log in]({{ url_for('login') }})
{% endif %}

Conditionals enable dynamic UI changes based on the data.

c. Accessing Data Structures

  • Dictionaries: {{ product['name'] }} or {{ product.name }}
  • Tuples/Lists: {{ product[0] }} (index‑based)

A well‑structured dataset makes the template cleaner and easier to maintain.


4. Separation of Concerns

  • Flask – handles data retrieval, business logic, and preparation of context variables.
  • Jinja – focuses solely on presentation.

This division allows you to modify the UI without touching data‑access code, and vice‑versa, improving maintainability and scalability.


5. Security

Jinja automatically escapes HTML‑unsafe characters, protecting against XSS. Nevertheless, you should:

  • Only pass the data the template truly needs.
  • Validate and sanitize any user‑generated content before storing it in the database.
  • Use |safe sparingly and only when you’re certain the content is trustworthy.

6. Real‑Time Updates

Whenever the underlying database changes (e.g., a new product is added), the next page load will fetch the latest data and render it, keeping the UI in sync with the backend.


7. Summary

  1. Extract data from the database in a Flask view.
  2. Pass the data as named variables to a Jinja template.
  3. Render the variables with loops, conditionals, and proper data‑structure access.

This workflow is the backbone of full‑stack Flask development and enables interactive, data‑driven web applications.


Example Jinja Template (Producer Dashboard)

{% extends 'base.html' %}
{% block title %}Producer Dashboard{% endblock %}

{% block extra_style %}

  .dashboard {
    padding: 2rem 6%;
  }

  .header {
    margin-bottom: 2rem;
  }

  .header h1 {
    font-size: 1.8rem;
    color: var(--gd);
  }

  .grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 1.5rem;
  }

  /* CARDS */
  .card {
    background: var(--w);
    border-radius: 14px;
    padding: 1.4rem;
    box-shadow: 0 6px 18px rgba(0,0,0,.08);
  }

  .card h2 {
    margin-bottom: 1rem;
    font-size: 1.2rem;
  }

  /* TABLE */
  .table {
    width: 100%;
    border-collapse: collapse;
    font-size: .85rem;
  }

  .table th {
    text-align: left;
    padding: .5rem;
    border-bottom: 2px solid var(--b);
  }

  .table td {
    padding: .5rem;
    border-bottom: 1px solid var(--b);
  }

  /* BADGES */
  .badge {
    padding: .25rem .6rem;
    border-radius: 20px;
    font-size: .75rem;
    font-weight: 700;
  }

  .pending   { background:#ffe7b3; }
  .completed { background:#c8f7d4; }
  .cancelled { background:#ffd2d2; }

  /* INPUT */
  .input {
    width: 70px;
    padding: .3rem;
    border: 1px solid var(--b);
    border-radius: 6px;
  }

  /* SEARCH */
  .search {
    margin-bottom: 1rem;
    padding: .5rem;
    width: 100%;
    border-radius: 8px;
    border: 1px solid var(--b);
  }

  /* BUTTON SMALL */
  .btn-small {
    padding: .3rem .7rem;
    font-size: .75rem;
    border-radius: 6px;
    background: var(--g);
    color: white;
    border: none;
    cursor: pointer;
  }

{% endblock %}

{% block content %}

  

{% endblock %}

The template retains the original structure and styling while being presented in a clean, readable markdown format.


  
## Welcome back, {{ producer_name }} 👋

  
Manage your products, stock, and orders

  
  
    
## Your Products

    

    
      
        
          Name
          Price
          Available
        
      
      
        {% for p in products %}
        
          {{ p[1] }}
          £{{ p[2] }}
          {{ 'Yes' if p[4] else 'No' }}
        
        {% endfor %}
      
    
  

  
  
    
## Stock Management

    
      
        
          Product
          Qty
          Update
        
      
      
        {% for s in stock %}
        
          {{ s[0] }}
          {{ s[1] }}
          
            
              
              
              Save
            
          
        
        {% endfor %}
      
    
  

  
  
    
## Orders

    

    
      
        
          ID
          Customer
          Product
          Qty
          Status
          Update
        
      
      
        {% for o in orders %}
        
          #{{ o[0] }}
          {{ o[1] }}
          {{ o[2] }}
          {{ o[3] }}
          
            {{ o[4] }}
          
          
            
              
              
                Pending
                Completed
                Cancelled
              
              Update
            
          
        
        {% endfor %}
      
    
  

/* SIMPLE TABLE FILTER */
function filterTable(input, tableId) {
  const filter = input.value.toLowerCase();
  const rows = document.querySelectorAll(`#${tableId} tbody tr`);

  rows.forEach(row => {
    const text = row.innerText.toLowerCase();
    row.style.display = text.includes(filter) ? '' : 'none';
  });
}

{% endblock %}
0 views
Back to Blog

Related posts

Read more »

Digital Debt Tracker

!Cover image for Digital Debt Trackerhttps://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s...