fastadmin 0.2.21__tar.gz → 0.3.0__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.
- {fastadmin-0.2.21 → fastadmin-0.3.0}/PKG-INFO +145 -59
- {fastadmin-0.2.21 → fastadmin-0.3.0}/README.md +136 -51
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/exceptions.py +2 -2
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/frameworks/django/app/api.py +27 -19
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/frameworks/fastapi/api.py +19 -8
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/frameworks/flask/api.py +28 -20
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/helpers.py +45 -8
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/schemas.py +1 -1
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/service.py +34 -15
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/models/base.py +35 -12
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/models/decorators.py +7 -4
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/models/orms/django.py +8 -8
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/models/orms/ponyorm.py +15 -7
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/models/orms/sqlalchemy.py +28 -15
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/models/orms/tortoise.py +2 -2
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/models/schemas.py +1 -1
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/static/index.min.js +1 -1
- {fastadmin-0.2.21 → fastadmin-0.3.0}/pyproject.toml +7 -6
- {fastadmin-0.2.21 → fastadmin-0.3.0}/LICENSE +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/__init__.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/__init__.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/frameworks/__init__.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/frameworks/django/__init__.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/frameworks/django/app/__init__.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/frameworks/django/app/urls.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/frameworks/django/app/views.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/frameworks/fastapi/__init__.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/frameworks/fastapi/app.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/frameworks/fastapi/views.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/frameworks/flask/__init__.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/frameworks/flask/app.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/api/frameworks/flask/views.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/models/__init__.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/models/helpers.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/models/orms/__init__.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/settings.py +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/static/assets/worker-6Z7niv9l.js +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/static/assets/worker-C151k0-L.js +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/static/assets/worker-D1fCgYB7.js +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/static/assets/worker-D3TWcJOI.js +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/static/assets/worker-DRNcaZ-V.js +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/static/assets/worker-MF2p-l5_.js +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/static/images/favicon.png +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/static/images/header-logo.svg +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/static/images/sign-in-logo.svg +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/static/index.html +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/static/index.min.css +0 -0
- {fastadmin-0.2.21 → fastadmin-0.3.0}/fastadmin/templates/index.html +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: fastadmin
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: FastAdmin is an easy-to-use Admin Dashboard App for FastAPI/Flask/Django inspired by Django Admin.
|
|
5
5
|
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Keywords: fastapi,admin
|
|
7
8
|
Author: Seva D
|
|
8
9
|
Author-email: vsdudakov@gmail.com
|
|
9
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.12,<4.0
|
|
10
11
|
Classifier: Environment :: Console
|
|
11
12
|
Classifier: Framework :: Django
|
|
12
13
|
Classifier: Framework :: FastAPI
|
|
@@ -14,10 +15,9 @@ Classifier: Framework :: Flask
|
|
|
14
15
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
16
|
Classifier: Operating System :: OS Independent
|
|
16
17
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
21
|
Classifier: Topic :: Software Development :: Documentation
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
23
|
Classifier: Topic :: Software Development :: Widget Sets
|
|
@@ -27,32 +27,32 @@ Provides-Extra: flask
|
|
|
27
27
|
Provides-Extra: pony
|
|
28
28
|
Provides-Extra: sqlalchemy
|
|
29
29
|
Provides-Extra: tortoise-orm
|
|
30
|
-
Requires-Dist: asgiref (>=3.
|
|
31
|
-
Requires-Dist: django (==
|
|
30
|
+
Requires-Dist: asgiref (>=3.11.1,<4.0.0)
|
|
31
|
+
Requires-Dist: django (==6.*) ; extra == "django"
|
|
32
32
|
Requires-Dist: fastapi (==0.*) ; extra == "fastapi"
|
|
33
33
|
Requires-Dist: flask (==3.*) ; extra == "flask"
|
|
34
|
+
Requires-Dist: greenlet (>=3.0,<4.0) ; extra == "sqlalchemy"
|
|
34
35
|
Requires-Dist: pony (==0.*) ; extra == "pony"
|
|
35
36
|
Requires-Dist: pyjwt (>=2.6.0,<3.0.0)
|
|
36
37
|
Requires-Dist: sqlalchemy[asyncio] (==2.*) ; extra == "sqlalchemy"
|
|
37
|
-
Requires-Dist: tortoise-orm (==
|
|
38
|
+
Requires-Dist: tortoise-orm (==1.*) ; extra == "tortoise-orm"
|
|
38
39
|
Project-URL: Homepage, https://github.com/vsdudakov/fastadmin
|
|
39
40
|
Project-URL: Repository, https://github.com/vsdudakov/fastadmin
|
|
40
41
|
Description-Content-Type: text/markdown
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
# Admin Dashboard for FastAPI / Flask / Django
|
|
43
44
|
|
|
44
45
|
[](https://codecov.io/gh/vsdudakov/fastadmin)
|
|
45
46
|
[](https://github.com/vsdudakov/fastadmin/blob/master/LICENSE)
|
|
46
47
|
[](https://pypi.org/project/fastadmin/)
|
|
47
|
-
[](https://www.python.org/downloads/release/python-3100/)
|
|
48
|
-
[](https://www.python.org/downloads/release/python-3110/)
|
|
49
48
|
[](https://www.python.org/downloads/release/python-3120/)
|
|
49
|
+
[](https://www.python.org/downloads/release/python-3130/)
|
|
50
50
|
|
|
51
51
|
## Screenshots
|
|
52
52
|
|
|
53
|
-

|
|
54
|
+

|
|
55
|
+

|
|
56
56
|
|
|
57
57
|
<p align="center">
|
|
58
58
|
<a href="https://twitter.com/intent/tweet?text=Admin%20Dashboard%20For%20FastAPI&url=https://github.com/vsdudakov/fastadmin&hashtags=FastAPI,AdminDashboard">
|
|
@@ -65,7 +65,7 @@ Description-Content-Type: text/markdown
|
|
|
65
65
|
## Introduction
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
<a href='https://github.com/vsdudakov/fastadmin' target='_blank'>FastAdmin</a> is an easy-to-use
|
|
68
|
+
<a href='https://github.com/vsdudakov/fastadmin' target='_blank'>FastAdmin</a> is an easy-to-use admin dashboard for FastAPI, Django, and Flask, inspired by Django Admin.
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
|
|
@@ -81,7 +81,7 @@ Description-Content-Type: text/markdown
|
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
FastAdmin
|
|
84
|
+
FastAdmin is built with relationships in mind and admiration for Django Admin. Its design focuses on making it as easy as possible to configure your admin dashboard for FastAPI, Django, or Flask.
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
|
|
@@ -97,7 +97,7 @@ FastAdmin was built with relations in mind and admiration for the excellent and
|
|
|
97
97
|
|
|
98
98
|
|
|
99
99
|
|
|
100
|
-
FastAdmin
|
|
100
|
+
FastAdmin aims to be minimal, functional, and familiar.
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
|
|
@@ -123,7 +123,7 @@ FastAdmin is designed to be minimalistic, functional and yet familiar.
|
|
|
123
123
|
|
|
124
124
|
|
|
125
125
|
|
|
126
|
-
If you have
|
|
126
|
+
If you have questions beyond this documentation, feel free to <a href='mailto:vsdudakov@gmail.com' target='_blank'>email us</a>.
|
|
127
127
|
|
|
128
128
|
|
|
129
129
|
|
|
@@ -141,7 +141,7 @@ If you have any questions that are beyond the scope of the documentation, Please
|
|
|
141
141
|
|
|
142
142
|
|
|
143
143
|
|
|
144
|
-
Follow the steps below to
|
|
144
|
+
Follow the steps below to set up FastAdmin:
|
|
145
145
|
|
|
146
146
|
|
|
147
147
|
|
|
@@ -155,7 +155,7 @@ Follow the steps below to setup FastAdmin:
|
|
|
155
155
|
|
|
156
156
|
|
|
157
157
|
|
|
158
|
-
Install the package
|
|
158
|
+
Install the package with pip:
|
|
159
159
|
|
|
160
160
|
|
|
161
161
|
|
|
@@ -175,7 +175,7 @@ Install the package using pip:
|
|
|
175
175
|
|
|
176
176
|
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
On zsh and macOS, use quotes: <code>pip install 'fastadmin[fastapi,django]'</code>
|
|
179
179
|
|
|
180
180
|
|
|
181
181
|
|
|
@@ -197,13 +197,13 @@ Note: For zsh and macos use: <code>pip install fastadmin[fastapi,django]</code>
|
|
|
197
197
|
|
|
198
198
|
```bash
|
|
199
199
|
|
|
200
|
-
pip install fastadmin[fastapi,django]
|
|
201
|
-
pip install fastadmin[fastapi,tortoise-orm] #
|
|
202
|
-
pip install fastadmin[fastapi,pony]
|
|
203
|
-
pip install fastadmin[fastapi,sqlalchemy]
|
|
204
|
-
pip install fastadmin[django]
|
|
205
|
-
pip install fastadmin[django,pony]
|
|
206
|
-
pip install fastadmin[flask,sqlalchemy]
|
|
200
|
+
pip install fastadmin[fastapi,django] # FastAPI with Django ORM
|
|
201
|
+
pip install fastadmin[fastapi,tortoise-orm] # FastAPI with Tortoise ORM
|
|
202
|
+
pip install fastadmin[fastapi,pony] # FastAPI with Pony ORM
|
|
203
|
+
pip install fastadmin[fastapi,sqlalchemy] # FastAPI with SQLAlchemy (includes greenlet)
|
|
204
|
+
pip install fastadmin[django] # Django with Django ORM
|
|
205
|
+
pip install fastadmin[django,pony] # Django with Pony ORM
|
|
206
|
+
pip install fastadmin[flask,sqlalchemy] # Flask with SQLAlchemy (includes greenlet)
|
|
207
207
|
|
|
208
208
|
```
|
|
209
209
|
|
|
@@ -213,7 +213,7 @@ pip install fastadmin[flask,sqlalchemy] # for flask with sqlalchemy
|
|
|
213
213
|
|
|
214
214
|
|
|
215
215
|
|
|
216
|
-
|
|
216
|
+
Or install with Poetry:
|
|
217
217
|
|
|
218
218
|
|
|
219
219
|
|
|
@@ -239,13 +239,13 @@ Install the package using poetry:
|
|
|
239
239
|
|
|
240
240
|
```bash
|
|
241
241
|
|
|
242
|
-
poetry add 'fastadmin[fastapi,django]'
|
|
243
|
-
poetry add 'fastadmin[fastapi,tortoise-orm]'
|
|
244
|
-
poetry add 'fastadmin[fastapi,pony]'
|
|
245
|
-
poetry add 'fastadmin[fastapi,sqlalchemy]'
|
|
246
|
-
poetry add 'fastadmin[django]'
|
|
247
|
-
poetry add 'fastadmin[django,pony]'
|
|
248
|
-
poetry add 'fastadmin[flask,sqlalchemy]'
|
|
242
|
+
poetry add 'fastadmin[fastapi,django]'
|
|
243
|
+
poetry add 'fastadmin[fastapi,tortoise-orm]'
|
|
244
|
+
poetry add 'fastadmin[fastapi,pony]'
|
|
245
|
+
poetry add 'fastadmin[fastapi,sqlalchemy]'
|
|
246
|
+
poetry add 'fastadmin[django]'
|
|
247
|
+
poetry add 'fastadmin[django,pony]'
|
|
248
|
+
poetry add 'fastadmin[flask,sqlalchemy]'
|
|
249
249
|
|
|
250
250
|
```
|
|
251
251
|
|
|
@@ -255,11 +255,11 @@ poetry add 'fastadmin[flask,sqlalchemy]' # for flask with sqlalchemy
|
|
|
255
255
|
|
|
256
256
|
|
|
257
257
|
|
|
258
|
-
Configure required settings using virtual environment variables:
|
|
259
258
|
|
|
260
259
|
|
|
261
260
|
|
|
262
261
|
|
|
262
|
+
When using SQLAlchemy, the <code>greenlet</code> package is required (included in the <code>fastadmin[sqlalchemy]</code> extra).
|
|
263
263
|
|
|
264
264
|
|
|
265
265
|
|
|
@@ -271,11 +271,27 @@ Configure required settings using virtual environment variables:
|
|
|
271
271
|
|
|
272
272
|
|
|
273
273
|
|
|
274
|
+
Configure the required settings with environment variables:
|
|
274
275
|
|
|
275
276
|
|
|
276
277
|
|
|
277
278
|
|
|
278
|
-
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
You can add these variables to a <code>.env</code> file and load them with python-dotenv. See <a href='https://vsdudakov.github.io/fastadmin#settings'>all settings</a> in the full documentation.
|
|
279
295
|
|
|
280
296
|
|
|
281
297
|
|
|
@@ -314,7 +330,7 @@ export ADMIN_SECRET_KEY=secret_key
|
|
|
314
330
|
|
|
315
331
|
|
|
316
332
|
|
|
317
|
-
|
|
333
|
+
Set up FastAdmin for your framework
|
|
318
334
|
|
|
319
335
|
|
|
320
336
|
|
|
@@ -475,13 +491,14 @@ Register ORM models
|
|
|
475
491
|
|
|
476
492
|
|
|
477
493
|
```python
|
|
494
|
+
import typing as tp
|
|
478
495
|
from uuid import UUID
|
|
479
496
|
|
|
480
497
|
import bcrypt
|
|
481
498
|
from tortoise import fields
|
|
482
499
|
from tortoise.models import Model
|
|
483
500
|
|
|
484
|
-
from fastadmin import TortoiseModelAdmin, register
|
|
501
|
+
from fastadmin import TortoiseModelAdmin, WidgetType, register
|
|
485
502
|
|
|
486
503
|
|
|
487
504
|
class User(Model):
|
|
@@ -489,6 +506,7 @@ class User(Model):
|
|
|
489
506
|
hash_password = fields.CharField(max_length=255)
|
|
490
507
|
is_superuser = fields.BooleanField(default=False)
|
|
491
508
|
is_active = fields.BooleanField(default=False)
|
|
509
|
+
avatar_url = fields.TextField(null=True)
|
|
492
510
|
|
|
493
511
|
def __str__(self):
|
|
494
512
|
return self.username
|
|
@@ -501,15 +519,39 @@ class UserAdmin(TortoiseModelAdmin):
|
|
|
501
519
|
list_display_links = ("id", "username")
|
|
502
520
|
list_filter = ("id", "username", "is_superuser", "is_active")
|
|
503
521
|
search_fields = ("username",)
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
522
|
+
formfield_overrides = { # noqa: RUF012
|
|
523
|
+
"username": (WidgetType.SlugInput, {"required": True}),
|
|
524
|
+
"password": (WidgetType.PasswordInput, {"passwordModalForm": True}),
|
|
525
|
+
"avatar_url": (
|
|
526
|
+
WidgetType.Upload,
|
|
527
|
+
{
|
|
528
|
+
"required": False,
|
|
529
|
+
# Disable crop image for upload field
|
|
530
|
+
# "disableCropImage": True,
|
|
531
|
+
},
|
|
532
|
+
),
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
async def authenticate(self, username: str, password: str) -> int | None:
|
|
536
|
+
user = await self.model_cls.filter(username=username, is_superuser=True).first()
|
|
507
537
|
if not user:
|
|
508
538
|
return None
|
|
509
539
|
if not bcrypt.checkpw(password.encode(), user.hash_password.encode()):
|
|
510
540
|
return None
|
|
511
541
|
return user.id
|
|
512
542
|
|
|
543
|
+
async def change_password(self, id: UUID | int | str, password: str) -> None:
|
|
544
|
+
user = await self.model_cls.filter(id=id).first()
|
|
545
|
+
if not user:
|
|
546
|
+
return
|
|
547
|
+
user.hash_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
|
|
548
|
+
await user.save(update_fields=("hash_password",))
|
|
549
|
+
|
|
550
|
+
async def orm_save_upload_field(self, obj: tp.Any, field: str, base64: str) -> None:
|
|
551
|
+
# convert base64 to bytes, upload to s3/filestorage, get url and save or save base64 as is to db (don't recomment it)
|
|
552
|
+
setattr(obj, field, base64)
|
|
553
|
+
await obj.save(update_fields=(field,))
|
|
554
|
+
|
|
513
555
|
```
|
|
514
556
|
|
|
515
557
|
|
|
@@ -569,7 +611,7 @@ class UserAdmin(DjangoModelAdmin):
|
|
|
569
611
|
|
|
570
612
|
|
|
571
613
|
|
|
572
|
-
###
|
|
614
|
+
### SQLAlchemy
|
|
573
615
|
|
|
574
616
|
|
|
575
617
|
|
|
@@ -583,8 +625,11 @@ class UserAdmin(DjangoModelAdmin):
|
|
|
583
625
|
|
|
584
626
|
|
|
585
627
|
```python
|
|
628
|
+
import typing as tp
|
|
629
|
+
import uuid
|
|
630
|
+
|
|
586
631
|
import bcrypt
|
|
587
|
-
from sqlalchemy import Boolean, Integer, String, select
|
|
632
|
+
from sqlalchemy import Boolean, Integer, String, Text, select, update
|
|
588
633
|
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
|
|
589
634
|
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
|
590
635
|
|
|
@@ -609,6 +654,7 @@ class User(Base):
|
|
|
609
654
|
hash_password: Mapped[str] = mapped_column(String(length=255), nullable=False)
|
|
610
655
|
is_superuser: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
|
611
656
|
is_active: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
|
657
|
+
avatar_url: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
612
658
|
|
|
613
659
|
def __str__(self):
|
|
614
660
|
return self.username
|
|
@@ -622,17 +668,33 @@ class UserAdmin(SqlAlchemyModelAdmin):
|
|
|
622
668
|
list_filter = ("id", "username", "is_superuser", "is_active")
|
|
623
669
|
search_fields = ("username",)
|
|
624
670
|
|
|
625
|
-
async def authenticate(self, username, password):
|
|
671
|
+
async def authenticate(self, username: str, password: str) -> uuid.UUID | int | None:
|
|
626
672
|
sessionmaker = self.get_sessionmaker()
|
|
627
673
|
async with sessionmaker() as session:
|
|
628
|
-
query = select(
|
|
674
|
+
query = select(self.model_cls).filter_by(username=username, password=password, is_superuser=True)
|
|
629
675
|
result = await session.scalars(query)
|
|
630
|
-
|
|
631
|
-
if not
|
|
676
|
+
obj = result.first()
|
|
677
|
+
if not obj:
|
|
632
678
|
return None
|
|
633
|
-
if not bcrypt.checkpw(password.encode(),
|
|
679
|
+
if not bcrypt.checkpw(password.encode(), obj.hash_password.encode()):
|
|
634
680
|
return None
|
|
635
|
-
return
|
|
681
|
+
return obj.id
|
|
682
|
+
|
|
683
|
+
async def change_password(self, id: uuid.UUID | int | str, password: str) -> None:
|
|
684
|
+
sessionmaker = self.get_sessionmaker()
|
|
685
|
+
async with sessionmaker() as session:
|
|
686
|
+
hash_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
|
|
687
|
+
query = update(self.model_cls).where(User.id.in_([id])).values(hash_password=hash_password)
|
|
688
|
+
await session.execute(query)
|
|
689
|
+
await session.commit()
|
|
690
|
+
|
|
691
|
+
async def orm_save_upload_field(self, obj: tp.Any, field: str, base64: str) -> None:
|
|
692
|
+
sessionmaker = self.get_sessionmaker()
|
|
693
|
+
async with sessionmaker() as session:
|
|
694
|
+
# convert base64 to bytes, upload to s3/filestorage, get url and save or save base64 as is to db (don't recomment it)
|
|
695
|
+
query = update(self.model_cls).where(User.id.in_([obj.id])).values(**{field: base64})
|
|
696
|
+
await session.execute(query)
|
|
697
|
+
await session.commit()
|
|
636
698
|
|
|
637
699
|
```
|
|
638
700
|
|
|
@@ -655,8 +717,11 @@ class UserAdmin(SqlAlchemyModelAdmin):
|
|
|
655
717
|
|
|
656
718
|
|
|
657
719
|
```python
|
|
720
|
+
import typing as tp
|
|
721
|
+
import uuid
|
|
722
|
+
|
|
658
723
|
import bcrypt
|
|
659
|
-
from pony.orm import Database, PrimaryKey, Required, db_session
|
|
724
|
+
from pony.orm import Database, LongStr, Optional, PrimaryKey, Required, commit, db_session
|
|
660
725
|
|
|
661
726
|
from fastadmin import PonyORMModelAdmin, register
|
|
662
727
|
|
|
@@ -671,6 +736,7 @@ class User(db.Entity): # type: ignore [name-defined]
|
|
|
671
736
|
hash_password = Required(str)
|
|
672
737
|
is_superuser = Required(bool, default=False)
|
|
673
738
|
is_active = Required(bool, default=False)
|
|
739
|
+
avatar_url = Optional(LongStr, nullable=True)
|
|
674
740
|
|
|
675
741
|
def __str__(self):
|
|
676
742
|
return self.username
|
|
@@ -685,13 +751,31 @@ class UserAdmin(PonyORMModelAdmin):
|
|
|
685
751
|
search_fields = ("username",)
|
|
686
752
|
|
|
687
753
|
@db_session
|
|
688
|
-
def authenticate(self, username, password):
|
|
689
|
-
|
|
690
|
-
if not
|
|
754
|
+
def authenticate(self, username: str, password: str) -> uuid.UUID | int | None:
|
|
755
|
+
obj = next((f for f in User.select(username=username, password=password, is_superuser=True)), None) # fmt: skip
|
|
756
|
+
if not obj:
|
|
691
757
|
return None
|
|
692
|
-
if not bcrypt.checkpw(password.encode(),
|
|
758
|
+
if not bcrypt.checkpw(password.encode(), obj.hash_password.encode()):
|
|
693
759
|
return None
|
|
694
|
-
return
|
|
760
|
+
return obj.id
|
|
761
|
+
|
|
762
|
+
@db_session
|
|
763
|
+
def change_password(self, id: uuid.UUID | int, password: str) -> None:
|
|
764
|
+
obj = next((f for f in self.model_cls.select(id=id)), None)
|
|
765
|
+
if not obj:
|
|
766
|
+
return
|
|
767
|
+
hash_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
|
|
768
|
+
obj.hash_password = hash_password
|
|
769
|
+
commit()
|
|
770
|
+
|
|
771
|
+
@db_session
|
|
772
|
+
def orm_save_upload_field(self, obj: tp.Any, field: str, base64: str) -> None:
|
|
773
|
+
obj = next((f for f in self.model_cls.select(id=obj.id)), None)
|
|
774
|
+
if not obj:
|
|
775
|
+
return
|
|
776
|
+
# convert base64 to bytes, upload to s3/filestorage, get url and save or save base64 as is to db (don't recomment it)
|
|
777
|
+
setattr(obj, field, base64)
|
|
778
|
+
commit()
|
|
695
779
|
|
|
696
780
|
```
|
|
697
781
|
|
|
@@ -717,7 +801,9 @@ class UserAdmin(PonyORMModelAdmin):
|
|
|
717
801
|
|
|
718
802
|
|
|
719
803
|
## Documentation
|
|
720
|
-
|
|
804
|
+
|
|
805
|
+
Full documentation is available at [vsdudakov.github.io/fastadmin](https://vsdudakov.github.io/fastadmin).
|
|
721
806
|
|
|
722
807
|
## License
|
|
723
|
-
|
|
808
|
+
|
|
809
|
+
This project is licensed under the MIT License — see the [LICENSE](https://github.com/vsdudakov/fastadmin/blob/main/LICENSE) file for details.
|