fastadmin 0.1.41__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. {fastadmin-0.1.41 → fastadmin-0.2.2}/PKG-INFO +23 -23
  2. {fastadmin-0.1.41 → fastadmin-0.2.2}/README.md +20 -19
  3. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/frameworks/django/app/api.py +2 -2
  4. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/frameworks/fastapi/api.py +2 -2
  5. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/frameworks/flask/api.py +14 -14
  6. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/frameworks/flask/app.py +2 -2
  7. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/helpers.py +13 -20
  8. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/service.py +5 -5
  9. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/models/base.py +19 -3
  10. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/models/decorators.py +1 -1
  11. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/models/helpers.py +1 -1
  12. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/models/orms/ponyorm.py +4 -5
  13. fastadmin-0.2.2/fastadmin/static/index.html +19 -0
  14. fastadmin-0.2.2/fastadmin/static/index.min.css +6 -0
  15. fastadmin-0.2.2/fastadmin/static/index.min.js +755 -0
  16. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/templates/index.html +2 -2
  17. {fastadmin-0.1.41 → fastadmin-0.2.2}/pyproject.toml +12 -12
  18. fastadmin-0.1.41/fastadmin/static/css/main.min.css +0 -7
  19. fastadmin-0.1.41/fastadmin/static/js/27.07bc2567.chunk.js +0 -1
  20. fastadmin-0.1.41/fastadmin/static/js/main.min.js +0 -2
  21. {fastadmin-0.1.41 → fastadmin-0.2.2}/LICENSE +0 -0
  22. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/__init__.py +0 -0
  23. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/__init__.py +0 -0
  24. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/exceptions.py +0 -0
  25. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/frameworks/__init__.py +0 -0
  26. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/frameworks/django/__init__.py +0 -0
  27. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/frameworks/django/app/__init__.py +0 -0
  28. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/frameworks/django/app/urls.py +0 -0
  29. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/frameworks/django/app/views.py +0 -0
  30. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/frameworks/fastapi/__init__.py +0 -0
  31. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/frameworks/fastapi/app.py +0 -0
  32. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/frameworks/fastapi/views.py +0 -0
  33. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/frameworks/flask/__init__.py +0 -0
  34. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/frameworks/flask/views.py +0 -0
  35. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/api/schemas.py +0 -0
  36. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/models/__init__.py +0 -0
  37. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/models/orms/__init__.py +0 -0
  38. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/models/orms/django.py +0 -0
  39. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/models/orms/sqlalchemy.py +0 -0
  40. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/models/orms/tortoise.py +0 -0
  41. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/models/schemas.py +0 -0
  42. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/settings.py +0 -0
  43. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/static/images/favicon.png +0 -0
  44. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/static/images/header-logo.svg +0 -0
  45. {fastadmin-0.1.41 → fastadmin-0.2.2}/fastadmin/static/images/sign-in-logo.svg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastadmin
3
- Version: 0.1.41
3
+ Version: 0.2.2
4
4
  Summary:
5
5
  Home-page: https://github.com/vsdudakov/fastadmin
6
6
  License: MIT
@@ -17,6 +17,7 @@ Classifier: Operating System :: OS Independent
17
17
  Classifier: Programming Language :: Python :: 3
18
18
  Classifier: Programming Language :: Python :: 3.10
19
19
  Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
20
21
  Classifier: Topic :: Software Development :: Documentation
21
22
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
23
  Classifier: Topic :: Software Development :: Widget Sets
@@ -26,14 +27,13 @@ Provides-Extra: flask
26
27
  Provides-Extra: pony
27
28
  Provides-Extra: sqlalchemy
28
29
  Provides-Extra: tortoise-orm
29
- Requires-Dist: asgiref (>=3.6.0,<4.0.0)
30
+ Requires-Dist: asgiref (==3.6.0)
30
31
  Requires-Dist: django (==4.*) ; extra == "django"
31
32
  Requires-Dist: fastapi (==0.*) ; extra == "fastapi"
32
33
  Requires-Dist: flask (==2.*) ; extra == "flask"
33
- Requires-Dist: isort (>=5.12.0,<6.0.0)
34
+ Requires-Dist: greenlet (>=3.0.3,<4.0.0)
34
35
  Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
35
36
  Requires-Dist: pony (==0.*) ; extra == "pony"
36
- Requires-Dist: pydantic (>=1.10.7,<2.0.0)
37
37
  Requires-Dist: pyjwt (>=2.6.0,<3.0.0)
38
38
  Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
39
39
  Requires-Dist: sqlalchemy (==2.*) ; extra == "sqlalchemy"
@@ -43,11 +43,12 @@ Description-Content-Type: text/markdown
43
43
 
44
44
  ## Admin Dashboard App for FastAPI/Flask/Django
45
45
 
46
- [![Build Status](https://github.com/vsdudakov/fastadmin/workflows/CI/badge.svg?branch=main)](https://github.com/vsdudakov/fastadmin/workflows/CI/badge.svg?branch=main)
47
46
  [![codecov](https://codecov.io/gh/vsdudakov/fastadmin/branch/main/graph/badge.svg?token=RNGX5HOW3T)](https://codecov.io/gh/vsdudakov/fastadmin)
48
47
  [![License](https://img.shields.io/github/license/vsdudakov/fastadmin)](https://github.com/vsdudakov/fastadmin/blob/master/LICENSE)
49
48
  [![PyPi](https://badgen.net/pypi/v/fastadmin)](https://pypi.org/project/fastadmin/)
50
49
  [![Python 3.10](https://img.shields.io/badge/python-3.10-blue.svg)](https://www.python.org/downloads/release/python-3100/)
50
+ [![Python 3.11](https://img.shields.io/badge/python-3.11-blue.svg)](https://www.python.org/downloads/release/python-3110/)
51
+ [![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/release/python-3120/)
51
52
 
52
53
  ## Screenshots
53
54
 
@@ -65,7 +66,7 @@ Description-Content-Type: text/markdown
65
66
 
66
67
  ## Introduction
67
68
 
68
-
69
+
69
70
  <a href='https://github.com/vsdudakov/fastadmin' target='_blank'>FastAdmin</a> is an easy-to-use Admin Dashboard App for FastAPI/Django/Flask inspired by Django Admin.
70
71
 
71
72
 
@@ -81,7 +82,7 @@ Description-Content-Type: text/markdown
81
82
 
82
83
 
83
84
 
84
-
85
+
85
86
  FastAdmin was built with relations in mind and admiration for the excellent and popular Django Admin. It's engraved in its design that you may configure your admin dashboard for FastAPI/Django/Flask easiest way.
86
87
 
87
88
 
@@ -97,7 +98,7 @@ FastAdmin was built with relations in mind and admiration for the excellent and
97
98
 
98
99
 
99
100
 
100
-
101
+
101
102
  FastAdmin is designed to be minimalistic, functional and yet familiar.
102
103
 
103
104
 
@@ -119,7 +120,7 @@ FastAdmin is designed to be minimalistic, functional and yet familiar.
119
120
 
120
121
  ## Getting Started
121
122
 
122
-
123
+
123
124
 
124
125
 
125
126
 
@@ -139,7 +140,7 @@ If you have any questions that are beyond the scope of the documentation, Please
139
140
 
140
141
  ### Installation
141
142
 
142
-
143
+
143
144
 
144
145
 
145
146
  Follow the steps below to setup FastAdmin:
@@ -155,7 +156,7 @@ Follow the steps below to setup FastAdmin:
155
156
 
156
157
 
157
158
 
158
-
159
+
159
160
  Install the package using pip:
160
161
 
161
162
 
@@ -171,7 +172,7 @@ Install the package using pip:
171
172
 
172
173
 
173
174
 
174
-
175
+
175
176
 
176
177
 
177
178
 
@@ -187,7 +188,7 @@ Note: For zsh and macos use: <code>pip install fastadmin[fastapi,django]</code>
187
188
 
188
189
 
189
190
 
190
-
191
+
191
192
 
192
193
 
193
194
 
@@ -213,7 +214,7 @@ pip install fastadmin[flask,sqlalchemy] # for flask with sqlalchemy
213
214
 
214
215
 
215
216
 
216
-
217
+
217
218
  Install the package using poetry:
218
219
 
219
220
 
@@ -229,7 +230,7 @@ Install the package using poetry:
229
230
 
230
231
 
231
232
 
232
-
233
+
233
234
 
234
235
 
235
236
 
@@ -255,7 +256,7 @@ poetry add 'fastadmin[flask,sqlalchemy]' # for flask with sqlalchemy
255
256
 
256
257
 
257
258
 
258
-
259
+
259
260
  Configure required settings using virtual environment variables:
260
261
 
261
262
 
@@ -271,7 +272,7 @@ Configure required settings using virtual environment variables:
271
272
 
272
273
 
273
274
 
274
-
275
+
275
276
 
276
277
 
277
278
 
@@ -287,7 +288,7 @@ Note: You can add these variables to .env and use python-dotenv to load them. Se
287
288
 
288
289
 
289
290
 
290
-
291
+
291
292
 
292
293
 
293
294
 
@@ -312,7 +313,7 @@ export ADMIN_SECRET_KEY=secret_key
312
313
 
313
314
  ### Quick Tutorial
314
315
 
315
-
316
+
316
317
 
317
318
 
318
319
  Setup FastAdmin for a framework
@@ -328,7 +329,7 @@ Setup FastAdmin for a framework
328
329
 
329
330
 
330
331
 
331
-
332
+
332
333
 
333
334
 
334
335
 
@@ -432,7 +433,7 @@ app.register_blueprint(admin_app, url_prefix="/admin")
432
433
 
433
434
 
434
435
 
435
-
436
+
436
437
 
437
438
 
438
439
  Register ORM models
@@ -448,7 +449,7 @@ Register ORM models
448
449
 
449
450
 
450
451
 
451
-
452
+
452
453
 
453
454
 
454
455
 
@@ -722,4 +723,3 @@ See full documentation [here](https://vsdudakov.github.io/fastadmin).
722
723
 
723
724
  ## License
724
725
  This project is licensed under the MIT License - see the [LICENSE](https://github.com/vsdudakov/fastadmin/blob/main/LICENSE) file for details.
725
-
@@ -1,10 +1,11 @@
1
1
  ## Admin Dashboard App for FastAPI/Flask/Django
2
2
 
3
- [![Build Status](https://github.com/vsdudakov/fastadmin/workflows/CI/badge.svg?branch=main)](https://github.com/vsdudakov/fastadmin/workflows/CI/badge.svg?branch=main)
4
3
  [![codecov](https://codecov.io/gh/vsdudakov/fastadmin/branch/main/graph/badge.svg?token=RNGX5HOW3T)](https://codecov.io/gh/vsdudakov/fastadmin)
5
4
  [![License](https://img.shields.io/github/license/vsdudakov/fastadmin)](https://github.com/vsdudakov/fastadmin/blob/master/LICENSE)
6
5
  [![PyPi](https://badgen.net/pypi/v/fastadmin)](https://pypi.org/project/fastadmin/)
7
6
  [![Python 3.10](https://img.shields.io/badge/python-3.10-blue.svg)](https://www.python.org/downloads/release/python-3100/)
7
+ [![Python 3.11](https://img.shields.io/badge/python-3.11-blue.svg)](https://www.python.org/downloads/release/python-3110/)
8
+ [![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/release/python-3120/)
8
9
 
9
10
  ## Screenshots
10
11
 
@@ -22,7 +23,7 @@
22
23
 
23
24
  ## Introduction
24
25
 
25
-
26
+
26
27
  <a href='https://github.com/vsdudakov/fastadmin' target='_blank'>FastAdmin</a> is an easy-to-use Admin Dashboard App for FastAPI/Django/Flask inspired by Django Admin.
27
28
 
28
29
 
@@ -38,7 +39,7 @@
38
39
 
39
40
 
40
41
 
41
-
42
+
42
43
  FastAdmin was built with relations in mind and admiration for the excellent and popular Django Admin. It's engraved in its design that you may configure your admin dashboard for FastAPI/Django/Flask easiest way.
43
44
 
44
45
 
@@ -54,7 +55,7 @@ FastAdmin was built with relations in mind and admiration for the excellent and
54
55
 
55
56
 
56
57
 
57
-
58
+
58
59
  FastAdmin is designed to be minimalistic, functional and yet familiar.
59
60
 
60
61
 
@@ -76,7 +77,7 @@ FastAdmin is designed to be minimalistic, functional and yet familiar.
76
77
 
77
78
  ## Getting Started
78
79
 
79
-
80
+
80
81
 
81
82
 
82
83
 
@@ -96,7 +97,7 @@ If you have any questions that are beyond the scope of the documentation, Please
96
97
 
97
98
  ### Installation
98
99
 
99
-
100
+
100
101
 
101
102
 
102
103
  Follow the steps below to setup FastAdmin:
@@ -112,7 +113,7 @@ Follow the steps below to setup FastAdmin:
112
113
 
113
114
 
114
115
 
115
-
116
+
116
117
  Install the package using pip:
117
118
 
118
119
 
@@ -128,7 +129,7 @@ Install the package using pip:
128
129
 
129
130
 
130
131
 
131
-
132
+
132
133
 
133
134
 
134
135
 
@@ -144,7 +145,7 @@ Note: For zsh and macos use: <code>pip install fastadmin[fastapi,django]</code>
144
145
 
145
146
 
146
147
 
147
-
148
+
148
149
 
149
150
 
150
151
 
@@ -170,7 +171,7 @@ pip install fastadmin[flask,sqlalchemy] # for flask with sqlalchemy
170
171
 
171
172
 
172
173
 
173
-
174
+
174
175
  Install the package using poetry:
175
176
 
176
177
 
@@ -186,7 +187,7 @@ Install the package using poetry:
186
187
 
187
188
 
188
189
 
189
-
190
+
190
191
 
191
192
 
192
193
 
@@ -212,7 +213,7 @@ poetry add 'fastadmin[flask,sqlalchemy]' # for flask with sqlalchemy
212
213
 
213
214
 
214
215
 
215
-
216
+
216
217
  Configure required settings using virtual environment variables:
217
218
 
218
219
 
@@ -228,7 +229,7 @@ Configure required settings using virtual environment variables:
228
229
 
229
230
 
230
231
 
231
-
232
+
232
233
 
233
234
 
234
235
 
@@ -244,7 +245,7 @@ Note: You can add these variables to .env and use python-dotenv to load them. Se
244
245
 
245
246
 
246
247
 
247
-
248
+
248
249
 
249
250
 
250
251
 
@@ -269,7 +270,7 @@ export ADMIN_SECRET_KEY=secret_key
269
270
 
270
271
  ### Quick Tutorial
271
272
 
272
-
273
+
273
274
 
274
275
 
275
276
  Setup FastAdmin for a framework
@@ -285,7 +286,7 @@ Setup FastAdmin for a framework
285
286
 
286
287
 
287
288
 
288
-
289
+
289
290
 
290
291
 
291
292
 
@@ -389,7 +390,7 @@ app.register_blueprint(admin_app, url_prefix="/admin")
389
390
 
390
391
 
391
392
 
392
-
393
+
393
394
 
394
395
 
395
396
  Register ORM models
@@ -405,7 +406,7 @@ Register ORM models
405
406
 
406
407
 
407
408
 
408
-
409
+
409
410
 
410
411
 
411
412
 
@@ -678,4 +679,4 @@ class UserAdmin(PonyORMModelAdmin):
678
679
  See full documentation [here](https://vsdudakov.github.io/fastadmin).
679
680
 
680
681
  ## License
681
- This project is licensed under the MIT License - see the [LICENSE](https://github.com/vsdudakov/fastadmin/blob/main/LICENSE) file for details.
682
+ This project is licensed under the MIT License - see the [LICENSE](https://github.com/vsdudakov/fastadmin/blob/main/LICENSE) file for details.
@@ -1,7 +1,7 @@
1
1
  import json
2
2
  import logging
3
3
  from dataclasses import asdict
4
- from datetime import datetime
4
+ from datetime import datetime, time
5
5
  from functools import wraps
6
6
  from uuid import UUID
7
7
 
@@ -23,7 +23,7 @@ api_service = ApiService()
23
23
 
24
24
  class JsonEncoder(DjangoJSONEncoder):
25
25
  def default(self, o):
26
- if isinstance(o, datetime):
26
+ if isinstance(o, datetime | time):
27
27
  return o.isoformat()
28
28
  if isinstance(o, UUID):
29
29
  return str(o)
@@ -117,7 +117,7 @@ async def list_objs(
117
117
  request: Request,
118
118
  model: str,
119
119
  search: str | None = None,
120
- sort_by: str = None,
120
+ sort_by: str | None = None,
121
121
  offset: int | None = 0,
122
122
  limit: int | None = 10,
123
123
  ):
@@ -238,7 +238,7 @@ async def export(
238
238
  model: str,
239
239
  payload: ExportInputSchema,
240
240
  search: str | None = None,
241
- sort_by: str = None,
241
+ sort_by: str | None = None,
242
242
  ):
243
243
  """This method is used to export a list of objects.
244
244
 
@@ -37,7 +37,7 @@ async def sign_in() -> Response:
37
37
  except AdminApiException as e:
38
38
  http_exception = HTTPException(e.detail)
39
39
  http_exception.code = e.status_code
40
- raise http_exception
40
+ raise http_exception from e
41
41
 
42
42
 
43
43
  @api_router.route("/sign-out", methods=["POST"])
@@ -57,7 +57,7 @@ async def sign_out() -> Response:
57
57
  except AdminApiException as e:
58
58
  http_exception = HTTPException(e.detail)
59
59
  http_exception.code = e.status_code
60
- raise http_exception
60
+ raise http_exception from e
61
61
 
62
62
 
63
63
  @api_router.route("/me", methods=["GET"])
@@ -79,7 +79,7 @@ async def me() -> dict:
79
79
  except AdminApiException as e:
80
80
  http_exception = HTTPException(e.detail)
81
81
  http_exception.code = e.status_code
82
- raise http_exception
82
+ raise http_exception from e
83
83
 
84
84
 
85
85
  @api_router.route("/dashboard-widget/<string:model>", methods=["GET"])
@@ -109,7 +109,7 @@ async def dashboard_widget(model: str) -> dict:
109
109
  except AdminApiException as e:
110
110
  http_exception = HTTPException(e.detail)
111
111
  http_exception.code = e.status_code
112
- raise http_exception
112
+ raise http_exception from e
113
113
 
114
114
 
115
115
  @api_router.route("/list/<string:model>", methods=["GET"])
@@ -143,14 +143,14 @@ async def list_objs(model: str) -> dict:
143
143
  "total": total,
144
144
  "results": objs,
145
145
  }
146
- except ValueError:
146
+ except ValueError as e:
147
147
  http_exception = HTTPException("Invalid format of get parameters")
148
148
  http_exception.code = 422
149
- raise http_exception
149
+ raise http_exception from e
150
150
  except AdminApiException as e:
151
151
  http_exception = HTTPException(e.detail)
152
152
  http_exception.code = e.status_code
153
- raise http_exception
153
+ raise http_exception from e
154
154
 
155
155
 
156
156
  @api_router.route("/retrieve/<string:model>/<string:id>", methods=["GET"])
@@ -174,7 +174,7 @@ async def get(model: str, id: UUID | int) -> dict:
174
174
  except AdminApiException as e:
175
175
  http_exception = HTTPException(e.detail)
176
176
  http_exception.code = e.status_code
177
- raise http_exception
177
+ raise http_exception from e
178
178
 
179
179
 
180
180
  @api_router.route("/add/<string:model>", methods=["POST"])
@@ -195,7 +195,7 @@ async def add(model: str) -> dict:
195
195
  except AdminApiException as e:
196
196
  http_exception = HTTPException(e.detail)
197
197
  http_exception.code = e.status_code
198
- raise http_exception
198
+ raise http_exception from e
199
199
 
200
200
 
201
201
  @api_router.route("/change-password/<string:id>", methods=["PATCH"]) # type: ignore [type-var]
@@ -221,7 +221,7 @@ async def change_password(id: UUID | int) -> UUID | int:
221
221
  except AdminApiException as e:
222
222
  http_exception = HTTPException(e.detail)
223
223
  http_exception.code = e.status_code
224
- raise http_exception
224
+ raise http_exception from e
225
225
 
226
226
 
227
227
  @api_router.route("/change/<string:model>/<string:id>", methods=["PATCH"])
@@ -248,7 +248,7 @@ async def change(model: str, id: UUID | int) -> dict:
248
248
  except AdminApiException as e:
249
249
  http_exception = HTTPException(e.detail)
250
250
  http_exception.code = e.status_code
251
- raise http_exception
251
+ raise http_exception from e
252
252
 
253
253
 
254
254
  @api_router.route("/export/<string:model>", methods=["POST"])
@@ -282,7 +282,7 @@ async def export(model: str) -> Response:
282
282
  except AdminApiException as e:
283
283
  http_exception = HTTPException(e.detail)
284
284
  http_exception.code = e.status_code
285
- raise http_exception
285
+ raise http_exception from e
286
286
 
287
287
 
288
288
  @api_router.route("/delete/<string:model>/<string:id>", methods=["DELETE"]) # type: ignore [type-var]
@@ -309,7 +309,7 @@ async def delete(
309
309
  except AdminApiException as e:
310
310
  http_exception = HTTPException(e.detail)
311
311
  http_exception.code = e.status_code
312
- raise http_exception
312
+ raise http_exception from e
313
313
 
314
314
 
315
315
  @api_router.route("/action/<string:model>/<string:action>", methods=["POST"])
@@ -337,7 +337,7 @@ async def action(
337
337
  except AdminApiException as e:
338
338
  http_exception = HTTPException(e.detail)
339
339
  http_exception.code = e.status_code
340
- raise http_exception
340
+ raise http_exception from e
341
341
 
342
342
 
343
343
  @api_router.route("/configuration", methods=["GET"])
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from datetime import date
2
+ from datetime import date, time
3
3
 
4
4
  from flask import Blueprint
5
5
  from flask.json.provider import DefaultJSONProvider
@@ -14,7 +14,7 @@ logger = logging.getLogger(__name__)
14
14
 
15
15
  class JSONProvider(DefaultJSONProvider):
16
16
  def default(self, o):
17
- if isinstance(o, date):
17
+ if isinstance(o, date | time):
18
18
  return o.isoformat()
19
19
  return super().default(o)
20
20
 
@@ -30,12 +30,11 @@ def sanitize_filter_key(key: str, fields: list[ModelFieldWidgetSchema]) -> tuple
30
30
  :return: A tuple of sanitized key and condition.
31
31
  """
32
32
  if "__" not in key:
33
- key = f"{key}__exact"
34
- field_name = key.split("__", 1)[0]
35
- condition = key.split("__", 1)[1]
33
+ key += "__exact"
34
+ field_name, _, condition = key.partition("__")
36
35
  field: ModelFieldWidgetSchema | None = next((field for field in fields if field.name == field_name), None)
37
36
  if field and field.filter_widget_props.get("parentModel") and not field.is_m2m:
38
- field_name = f"{field_name}_id"
37
+ field_name += "_id"
39
38
  return field_name, condition
40
39
 
41
40
 
@@ -52,32 +51,26 @@ def is_valid_uuid(uuid_to_test: str) -> bool:
52
51
  return str(uuid_obj) == uuid_to_test
53
52
 
54
53
 
55
- def is_digit(digit_to_test: str) -> bool:
56
- """Check if digit_to_test is a digit.
57
-
58
- :param digit_to_test: A digit to test.
59
- :return: True if digit_to_test is a digit, False otherwise.
60
- """
61
- try:
62
- int(digit_to_test)
63
- except ValueError:
64
- return False
65
- return True
66
-
67
-
68
54
  def is_valid_id(id: UUID | int) -> bool:
69
55
  """Check if id is a valid id.
70
56
 
71
57
  :param id: An id to test.
72
58
  :return: True if id is a valid id, False otherwise.
73
59
  """
74
- return is_digit(str(id)) or is_valid_uuid(str(id))
60
+ if is_valid_uuid(str(id)):
61
+ return True
62
+ try:
63
+ int(id)
64
+ return True
65
+ except ValueError:
66
+ pass
67
+ return False
75
68
 
76
69
 
77
70
  def is_valid_base64(value: str) -> bool:
78
- """Check if s is a valid base64.
71
+ """Check if a string is a valid base64.
79
72
 
80
- :param s: A string to test.
73
+ :param value: A string to test.
81
74
  :return: True if s is a valid base64, False otherwise.
82
75
  """
83
76
  try:
@@ -107,7 +107,7 @@ class ApiService:
107
107
  if inspect.iscoroutinefunction(admin_model.authenticate):
108
108
  authenticate_fn = admin_model.authenticate
109
109
  else:
110
- authenticate_fn = sync_to_async(admin_model.authenticate)
110
+ authenticate_fn = sync_to_async(admin_model.authenticate) # type: ignore [arg-type]
111
111
 
112
112
  user_id = await authenticate_fn(payload.username, payload.password)
113
113
 
@@ -162,7 +162,7 @@ class ApiService:
162
162
  if inspect.iscoroutinefunction(dashboard_widget_model.get_data):
163
163
  get_data = dashboard_widget_model.get_data
164
164
  else:
165
- get_data = sync_to_async(dashboard_widget_model.get_data)
165
+ get_data = sync_to_async(dashboard_widget_model.get_data) # type: ignore [arg-type]
166
166
  data = await get_data(
167
167
  min_x_field=query_params.min_x_field,
168
168
  max_x_field=query_params.max_x_field,
@@ -178,7 +178,7 @@ class ApiService:
178
178
  model: str,
179
179
  search: str | None = None,
180
180
  sort_by: str | None = None,
181
- filters: dict = None,
181
+ filters: dict | None = None,
182
182
  offset: int | None = 0,
183
183
  limit: int | None = 10,
184
184
  ) -> tuple[list[dict], int]:
@@ -302,7 +302,7 @@ class ApiService:
302
302
  if inspect.iscoroutinefunction(admin_model.change_password):
303
303
  change_password_fn = admin_model.change_password
304
304
  else:
305
- change_password_fn = sync_to_async(admin_model.change_password)
305
+ change_password_fn = sync_to_async(admin_model.change_password) # type: ignore [arg-type]
306
306
  await change_password_fn(id, payload.password)
307
307
 
308
308
  async def change(
@@ -332,7 +332,7 @@ class ApiService:
332
332
  payload: ExportInputSchema,
333
333
  search: str | None = None,
334
334
  sort_by: str | None = None,
335
- filters: dict = None,
335
+ filters: dict | None = None,
336
336
  ) -> tuple[str, str, StringIO | BytesIO | None]:
337
337
  current_user_id = await get_user_id_from_session_id(session_id)
338
338
  if not current_user_id:
@@ -1,4 +1,5 @@
1
1
  import csv
2
+ import datetime
2
3
  import inspect
3
4
  import json
4
5
  from collections.abc import Sequence
@@ -87,7 +88,7 @@ class BaseModelAdmin:
87
88
  # formfield_overrides = {
88
89
  # "description": (WidgetType.RichTextArea, {})
89
90
  # }
90
- formfield_overrides: dict[str, tuple[WidgetType, dict]] = {}
91
+ formfield_overrides: dict[str, tuple[WidgetType, dict]] = {} # noqa: RUF012
91
92
 
92
93
  # Set list_display to control which fields are displayed on the list page of the admin.
93
94
  # If you don't set list_display, the admin site will display a single column that displays the __str__() representation of each object
@@ -104,7 +105,7 @@ class BaseModelAdmin:
104
105
  # list_display_widths = {
105
106
  # "id": "100px",
106
107
  # }
107
- list_display_widths: dict[str, str] = {}
108
+ list_display_widths: dict[str, str] = {} # noqa: RUF012
108
109
 
109
110
  # Set list_filter to activate filters in the tabel columns of the list page of the admin.
110
111
  # Example of usage: list_filter = ("is_superuser", "is_active", "created_at")
@@ -383,6 +384,17 @@ class BaseModelAdmin:
383
384
 
384
385
  return obj_dict
385
386
 
387
+ def deserialize_value(self, field: ModelFieldWidgetSchema, value: Any) -> Any:
388
+ if not value:
389
+ return value
390
+ match field.form_widget_type:
391
+ case WidgetType.TimePicker:
392
+ return datetime.datetime.fromisoformat(value).time()
393
+ case WidgetType.DatePicker | WidgetType.DateTimePicker:
394
+ return datetime.datetime.fromisoformat(value)
395
+ case _:
396
+ return value
397
+
386
398
  async def get_list(
387
399
  self,
388
400
  offset: int | None = None,
@@ -434,7 +446,11 @@ class BaseModelAdmin:
434
446
  m2m_fields = self.get_model_fields_with_widget_types(with_m2m=True)
435
447
  upload_fields = self.get_model_fields_with_widget_types(with_upload=True)
436
448
 
437
- fields_payload = {field.column_name: payload[field.name] for field in fields if field.name in payload}
449
+ fields_payload = {
450
+ field.column_name: self.deserialize_value(field, payload[field.name])
451
+ for field in fields
452
+ if field.name in payload
453
+ }
438
454
  obj = await self.orm_save_obj(id, fields_payload)
439
455
  if not obj:
440
456
  return None
@@ -1,4 +1,4 @@
1
- def action(function=None, *, description: str = None):
1
+ def action(function=None, *, description: str | None = None):
2
2
  """Conveniently add attributes to an action function:
3
3
 
4
4
  Example of usage: