Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add json export format. #829

Merged
merged 5 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/configurations.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ The export options can be set per model and includes the following options:
* `column_export_list`: List of columns to include in the export data. Default is all model columns.
* `column_export_exclude_list`: List of columns to exclude in the export data.
* `export_max_rows`: Maximum number of rows to be exported. Default value is `0` which means unlimited.
* `export_types`: List of export types to be enabled. Default value is `["csv"]`.
* `export_types`: List of export types to be enabled. Default value is `["csv","json"]`.

## Templates

Expand Down
31 changes: 29 additions & 2 deletions sqladmin/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import json
import time
import warnings
from enum import Enum
Expand Down Expand Up @@ -454,7 +455,7 @@ class UserAdmin(ModelView, model=User):
```
"""

export_types: ClassVar[List[str]] = ["csv"]
export_types: ClassVar[List[str]] = ["csv", "json"]
"""A list of available export filetypes.
Currently only `csv` is supported.
"""
Expand Down Expand Up @@ -1152,7 +1153,9 @@ async def export_data(
) -> StreamingResponse:
if export_type == "csv":
return await self._export_csv(data)
raise NotImplementedError("Only export_type='csv' is implemented.")
elif export_type == "json":
return await self._export_json(data)
raise NotImplementedError("Only export_type='csv' or 'json' is implemented.")

async def _export_csv(
self,
Expand All @@ -1179,6 +1182,30 @@ async def generate(writer: Writer) -> AsyncGenerator[Any, None]:
headers={"Content-Disposition": f"attachment;filename={filename}"},
)

async def _export_json(
self,
data: List[Any],
) -> StreamingResponse:
async def generate() -> AsyncGenerator[str, None]:
yield "["
separator = "," if len(data) > 1 else ""

for row in data:
row_dict = {
name: await self.get_prop_value(row, name)
for name in self._export_prop_names
}
yield json.dumps(row_dict) + separator

yield "]"

filename = secure_filename(self.get_export_name(export_type="json"))
return StreamingResponse(
content=generate(),
media_type="application/json",
headers={"Content-Disposition": f"attachment;filename={filename}"},
)

def _refresh_form_rules_cache(self) -> None:
if self.form_rules:
self._form_create_rules = self.form_rules
Expand Down
17 changes: 16 additions & 1 deletion tests/test_views/test_view_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -778,11 +778,26 @@ def row_count(resp) -> int:
assert row_count(response) == 3


async def test_export_json(client: AsyncClient) -> None:
async with session_maker() as session:
user = User(name="Daniel", status="ACTIVE")
session.add(user)
await session.commit()

response = await client.get("/admin/user/export/json")
assert response.text == '[{"name": "Daniel", "status": "ACTIVE"}]'


async def test_export_bad_type_is_404(client: AsyncClient) -> None:
response = await client.get("/admin/user/export/bad_type")
assert response.status_code == 404


async def test_export_permission(client: AsyncClient) -> None:
async def test_export_permission_csv(client: AsyncClient) -> None:
response = await client.get("/admin/movie/export/csv")
assert response.status_code == 403


async def test_export_permission_json(client: AsyncClient) -> None:
response = await client.get("/admin/movie/export/json")
assert response.status_code == 403
10 changes: 10 additions & 0 deletions tests/test_views/test_view_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,16 @@ def test_export_csv(client: TestClient) -> None:
assert response.text == "name,status\r\nDaniel,ACTIVE\r\n"


def test_export_json(client: TestClient) -> None:
with session_maker() as session:
user = User(name="Daniel", status="ACTIVE")
session.add(user)
session.commit()

response = client.get("/admin/user/export/json")
assert response.text == '[{"name": "Daniel", "status": "ACTIVE"}]'


def test_export_csv_row_count(client: TestClient) -> None:
def row_count(resp) -> int:
return resp.text.count("\r\n") - 1
Expand Down
Loading