Skip to content

Commit

Permalink
add json export format (#829)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vasiliy566 authored Oct 17, 2024
1 parent 3afc4bc commit 402780f
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 4 deletions.
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 @@ -1158,7 +1159,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 @@ -1185,6 +1188,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

0 comments on commit 402780f

Please sign in to comment.