Polling (Auto-refresh)¶
Charts can auto-refresh by fetching new data via HTTP at regular intervals. No WebSocket, no MQTT — just plain fetch().
How It Works¶
- Chart renders with initial data from the Django view
- JavaScript fetches the
poll_urleverypoll_intervalseconds - Chart re-renders with new data (animated transition)
Quick Example¶
# views.py
from d3_bridge import BarChart
from d3_bridge.views import ChartDataView
def dashboard(request):
chart = BarChart(
data=Product.objects.values("name", "stock"),
x="name", y="stock",
title="Current Stock",
poll_url="/api/chart/stock/",
poll_interval=15, # refresh every 15 seconds
)
return render(request, "dashboard.html", {"chart": chart})
# urls.py
from d3_bridge.views import ChartDataView
urlpatterns = [
path("api/chart/stock/", ChartDataView.as_view(
queryset=Product.objects.all(),
fields=["name", "stock"],
ordering="-stock",
)),
]
Replace vs Append Mode¶
Replace (default)¶
Each poll replaces all chart data:
chart = BarChart(
data=data, x="x", y="y",
poll_url="/api/data/",
poll_interval=30,
poll_replace=True, # default
)
Append¶
New data is appended to existing data, with a sliding window:
chart = LineChart(
data=initial_readings,
x="timestamp", y="value",
poll_url="/api/latest-reading/",
poll_interval=5,
poll_replace=False, # append mode
poll_window=100, # keep last 100 points
)
Custom Headers¶
chart = BarChart(
data=data, x="x", y="y",
poll_url="/api/data/",
poll_interval=30,
poll_headers={"X-Requested-With": "d3-bridge"},
)
Don't put secrets in poll_headers
Header values are embedded in clear text in the page's HTML (inside the chart's JSON config), visible to anyone who can view the page source. Never put API keys or bearer tokens there. Prefer Django's session/cookie authentication — the poller already sends same-origin credentials and the CSRF token automatically.
ChartDataView¶
A lightweight JSON endpoint included in the package. No Django REST Framework required.
from d3_bridge.views import ChartDataView
urlpatterns = [
path("api/chart/sales/", ChartDataView.as_view(
queryset=Sale.objects.all(),
fields=["date", "amount", "product"],
ordering="-date",
limit=100,
)),
]
| Option | Type | Description |
|---|---|---|
queryset |
QuerySet | Data source (re-evaluated each request) |
fields |
list | Fields to include |
ordering |
str or list | Order by field(s) |
limit |
int | Max records |
geojson |
bool | Serialize as GeoJSON |
Override get_queryset() or filter_queryset() for dynamic filtering:
class SalesByRegion(ChartDataView):
queryset = Sale.objects.all()
fields = ["product", "amount"]
def filter_queryset(self, qs):
region = self.request.GET.get("region")
if region:
qs = qs.filter(region=region)
return qs
Smart Behavior¶
- Pauses when tab is hidden — saves bandwidth (Page Visibility API)
- Exponential backoff — on network errors, waits progressively longer before retrying
- CSRF token — auto-detected from Django's cookie
- Same-origin credentials — included automatically
Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
poll_url |
str | None |
URL to fetch data from |
poll_interval |
int | 0 |
Seconds between fetches (0 = disabled) |
poll_replace |
bool | True |
Replace all data (True) or append (False) |
poll_window |
int | None |
Max data points in append mode |
poll_headers |
dict | None |
Custom HTTP headers |