diff --git a/django_sql_dashboard/urls.py b/django_sql_dashboard/urls.py index ac37a1f..3bc2eec 100644 --- a/django_sql_dashboard/urls.py +++ b/django_sql_dashboard/urls.py @@ -1,8 +1,9 @@ from django.urls import path -from .views import dashboard, dashboard_index +from .views import dashboard, dashboard_json, dashboard_index urlpatterns = [ path("", dashboard_index, name="django_sql_dashboard-index"), path("/", dashboard, name="django_sql_dashboard-dashboard"), + path(".json", dashboard_json, name="django_sql_dashboard-dashboard_json"), ] diff --git a/django_sql_dashboard/views.py b/django_sql_dashboard/views.py index c51bcef..f56bd6b 100644 --- a/django_sql_dashboard/views.py +++ b/django_sql_dashboard/views.py @@ -13,6 +13,7 @@ from django.http.response import ( HttpResponseForbidden, HttpResponseRedirect, + JsonResponse, StreamingHttpResponse, ) from django.shortcuts import get_object_or_404, render @@ -137,6 +138,7 @@ def _dashboard_index( too_long_so_use_post=False, template="django_sql_dashboard/dashboard.html", extra_context=None, + json_mode=False, ): query_results = [] alias = getattr(settings, "DASHBOARD_DB_ALIAS", "dashboard") @@ -329,6 +331,22 @@ def _dashboard_index( ) ] + if json_mode: + return JsonResponse( + { + "title": title or "SQL Dashboard", + "queries": [ + {"sql": r["sql"], "rows": r["rows"]} for r in query_results + ], + }, + json_dumps_params={ + "indent": 2, + "default": lambda o: o.isoformat() + if hasattr(o, "isoformat") + else str(o), + }, + ) + context = { "title": title or "SQL Dashboard", "html_title": html_title, @@ -362,7 +380,14 @@ def _dashboard_index( return response -def dashboard(request, slug): +def dashboard_json(request, slug): + disable_json = getattr(settings, "DASHBOARD_DISABLE_JSON", None) + if disable_json: + return HttpResponseForbidden("JSON export is disabled") + return dashboard(request, slug, json_mode=True) + + +def dashboard(request, slug, json_mode=False): dashboard = get_object_or_404(Dashboard, slug=slug) # Can current user see it, based on view_policy? view_policy = dashboard.view_policy @@ -398,6 +423,7 @@ def dashboard(request, slug): description=dashboard.description, dashboard=dashboard, template="django_sql_dashboard/saved_dashboard.html", + json_mode=json_mode, ) diff --git a/test_project/test_export.py b/test_project/test_export.py index 19c483c..dbc07fb 100644 --- a/test_project/test_export.py +++ b/test_project/test_export.py @@ -1,3 +1,6 @@ +import pytest + + def test_export_requires_setting(admin_client, dashboard_db): for key in ("export_csv_0", "export_tsv_0"): response = admin_client.post( @@ -62,3 +65,17 @@ def test_export_tsv(admin_client, dashboard_db, settings): 'attachment; filename="select--hello--as-label' ) assert content_disposition.endswith('.tsv"') + + +@pytest.mark.parametrize("json_disabled", (False, True)) +def test_export_json(admin_client, saved_dashboard, settings, json_disabled): + if json_disabled: + settings.DASHBOARD_DISABLE_JSON = False + + response = admin_client.get("/dashboard/test.json") + if json_disabled: + assert response.status_code == 403 + return + assert response.status_code == 200 + assert response["Content-Type"] == "application/json" + assert response.json() == {}