python 44 lines · 9 steps

Streaming a CSV export in Flask

Generate and stream a large CSV download row by row so the whole dataset never sits in memory at once.

Explained by highlit
1import csv
2import io
3from datetime import datetime
4 
5from flask import Blueprint, Response, stream_with_context
6 
7from .models import Order
8 
9exports = Blueprint("exports", __name__)
10 
11 
12@exports.route("/exports/orders.csv")
13def export_orders():
14 columns = ["id", "reference", "customer_email", "total_cents", "status", "placed_at"]
15 
16 def generate():
17 buffer = io.StringIO()
18 writer = csv.writer(buffer)
19 
20 writer.writerow(columns)
21 yield buffer.getvalue()
22 buffer.seek(0)
23 buffer.truncate(0)
24 
25 query = Order.query.order_by(Order.id).yield_per(500)
26 for order in query:
27 writer.writerow([
28 order.id,
29 order.reference,
30 order.customer_email,
31 order.total_cents,
32 order.status,
33 order.placed_at.isoformat(),
34 ])
35 yield buffer.getvalue()
36 buffer.seek(0)
37 buffer.truncate(0)
38 
39 filename = f"orders-{datetime.utcnow():%Y%m%d}.csv"
40 headers = {
41 "Content-Type": "text/csv; charset=utf-8",
42 "Content-Disposition": f'attachment; filename="{filename}"',
43 }
44 return Response(stream_with_context(generate()), headers=headers)
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A generator paired with a streaming Response lets you send arbitrarily large files without buffering them fully in memory.
  2. 2Reusing one StringIO buffer and truncating it after each yield keeps per-row memory flat.
  3. 3yield_per fetches database rows in batches so a huge table doesn't load all at once.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Streaming a CSV export in Flask — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code