ohmyapi 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl
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.
- ohmyapi/builtin/auth/models.py +8 -1
- ohmyapi/db/model/model.py +39 -73
- {ohmyapi-0.1.0.dist-info → ohmyapi-0.1.2.dist-info}/METADATA +30 -7
- {ohmyapi-0.1.0.dist-info → ohmyapi-0.1.2.dist-info}/RECORD +6 -6
- {ohmyapi-0.1.0.dist-info → ohmyapi-0.1.2.dist-info}/WHEEL +0 -0
- {ohmyapi-0.1.0.dist-info → ohmyapi-0.1.2.dist-info}/entry_points.txt +0 -0
ohmyapi/builtin/auth/models.py
CHANGED
@@ -1,23 +1,30 @@
|
|
1
1
|
from typing import Optional, List
|
2
2
|
from ohmyapi.db import Model, field
|
3
3
|
from passlib.context import CryptContext
|
4
|
+
from tortoise.contrib.pydantic import pydantic_queryset_creator
|
4
5
|
|
5
6
|
pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")
|
6
7
|
|
7
8
|
|
8
9
|
class Group(Model):
|
9
10
|
id = field.IntField(pk=True)
|
10
|
-
name = field.CharField(max_length=42)
|
11
|
+
name = field.CharField(max_length=42, index=True)
|
11
12
|
|
12
13
|
|
13
14
|
class User(Model):
|
14
15
|
id = field.IntField(pk=True)
|
16
|
+
email = CharField(unique=True, index=True)
|
15
17
|
username = field.CharField(max_length=150, unique=True)
|
16
18
|
password_hash = field.CharField(max_length=128)
|
17
19
|
is_admin = field.BooleanField(default=False)
|
18
20
|
is_staff = field.BooleanField(default=False)
|
19
21
|
groups: Optional[List[Group]] = field.ManyToManyField("ohmyapi_auth.Group", related_name="users")
|
20
22
|
|
23
|
+
|
24
|
+
class Schema:
|
25
|
+
exclude = 'password_hash',
|
26
|
+
|
27
|
+
|
21
28
|
def set_password(self, raw_password: str) -> None:
|
22
29
|
"""Hash and store the password."""
|
23
30
|
self.password_hash = pwd_context.hash(raw_password)
|
ohmyapi/db/model/model.py
CHANGED
@@ -3,78 +3,44 @@ from tortoise.models import Model as TortoiseModel
|
|
3
3
|
from tortoise.contrib.pydantic import pydantic_model_creator, pydantic_queryset_creator
|
4
4
|
|
5
5
|
|
6
|
-
class
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
class ModelMeta(type(TortoiseModel)):
|
7
|
+
def __new__(cls, name, bases, attrs):
|
8
|
+
new_cls = super().__new__(cls, name, bases, attrs)
|
9
|
+
|
10
|
+
schema_opts = getattr(new_cls, "Schema", None)
|
11
|
+
|
12
|
+
class BoundSchema:
|
13
|
+
@property
|
14
|
+
def one(self):
|
15
|
+
"""Return a Pydantic model class for 'one' results."""
|
16
|
+
include = getattr(schema_opts, "include", None)
|
17
|
+
exclude = getattr(schema_opts, "exclude", None)
|
18
|
+
return pydantic_model_creator(
|
19
|
+
new_cls,
|
20
|
+
name=f"{new_cls.__name__}SchemaOne",
|
21
|
+
include=include,
|
22
|
+
exclude=exclude,
|
23
|
+
exclude_readonly=True,
|
24
|
+
)
|
25
|
+
|
26
|
+
@property
|
27
|
+
def many(self):
|
28
|
+
"""Return a Pydantic queryset class for 'many' results."""
|
29
|
+
include = getattr(schema_opts, "include", None)
|
30
|
+
exclude = getattr(schema_opts, "exclude", None)
|
31
|
+
return pydantic_queryset_creator(
|
32
|
+
new_cls,
|
33
|
+
name=f"{new_cls.__name__}SchemaMany",
|
34
|
+
include=include,
|
35
|
+
exclude=exclude,
|
36
|
+
)
|
37
|
+
|
38
|
+
new_cls.Schema = BoundSchema()
|
39
|
+
return new_cls
|
40
|
+
|
41
|
+
|
42
|
+
class Model(TortoiseModel, metaclass=ModelMeta):
|
11
43
|
class Schema:
|
12
|
-
|
13
|
-
|
14
|
-
"""
|
15
|
-
|
16
|
-
def __init__(self, model_cls):
|
17
|
-
self.model_cls = model_cls
|
18
|
-
|
19
|
-
@property
|
20
|
-
def id(self):
|
21
|
-
# Minimal schema with just the primary key field
|
22
|
-
pk_field = self.model_cls._meta.pk_attr
|
23
|
-
return pydantic_model_creator(
|
24
|
-
self.model_cls, name=f"{self.model_cls.__name__}SchemaId", include=(pk_field,)
|
25
|
-
)
|
26
|
-
|
27
|
-
@property
|
28
|
-
def get(self):
|
29
|
-
# Full schema for reading
|
30
|
-
return pydantic_model_creator(
|
31
|
-
self.model_cls, name=f"{self.model_cls.__name__}SchemaGet"
|
32
|
-
)
|
33
|
-
|
34
|
-
@property
|
35
|
-
def post(self):
|
36
|
-
# Input schema for creation (no readonly fields like ID/PK)
|
37
|
-
return pydantic_model_creator(
|
38
|
-
self.model_cls,
|
39
|
-
name=f"{self.model_cls.__name__}SchemaPost",
|
40
|
-
exclude_readonly=True,
|
41
|
-
)
|
42
|
-
|
43
|
-
@property
|
44
|
-
def put(self):
|
45
|
-
# Input schema for updating
|
46
|
-
return pydantic_model_creator(
|
47
|
-
self.model_cls,
|
48
|
-
name=f"{self.model_cls.__name__}SchemaPut",
|
49
|
-
exclude_readonly=True,
|
50
|
-
)
|
51
|
-
|
52
|
-
@property
|
53
|
-
def delete(self):
|
54
|
-
# Schema for delete operations (just PK)
|
55
|
-
pk_field = self.model_cls._meta.pk_attr
|
56
|
-
return pydantic_model_creator(
|
57
|
-
self.model_cls, name=f"{self.model_cls.__name__}SchemaDelete", include=(pk_field,)
|
58
|
-
)
|
59
|
-
|
60
|
-
@property
|
61
|
-
def list(self):
|
62
|
-
# Schema for list endpoints
|
63
|
-
return pydantic_queryset_creator(self.model_cls)
|
64
|
-
|
65
|
-
def from_fields(self, *fields: str):
|
66
|
-
# Generate schema restricted to given fields
|
67
|
-
valid = [f for f in fields if f in self.model_cls._meta.fields_map]
|
68
|
-
return pydantic_model_creator(
|
69
|
-
self.model_cls,
|
70
|
-
name=f"{self.model_cls.__name__}SchemaFields",
|
71
|
-
include=valid,
|
72
|
-
)
|
73
|
-
|
74
|
-
def __init_subclass__(cls, **kwargs):
|
75
|
-
"""
|
76
|
-
Automatically attach .Schema to all subclasses
|
77
|
-
"""
|
78
|
-
super().__init_subclass__(**kwargs)
|
79
|
-
cls.Schema = cls.Schema(cls)
|
44
|
+
include = None
|
45
|
+
exclude = None
|
80
46
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ohmyapi
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.2
|
4
4
|
Summary: A Django-like but async web-framework based on FastAPI and TortoiseORM.
|
5
5
|
License-Expression: MIT
|
6
6
|
Keywords: fastapi,tortoise,orm,async,web-framework
|
@@ -28,8 +28,9 @@ Description-Content-Type: text/markdown
|
|
28
28
|
|
29
29
|
> OhMyAPI == Application scaffolding for FastAPI+TortoiseORM.
|
30
30
|
|
31
|
-
OhMyAPI is a
|
32
|
-
It is
|
31
|
+
OhMyAPI is a Django-flavored web-application scaffolding framework.
|
32
|
+
It is blazingly fast, async and has batteries included.
|
33
|
+
Built around FastAPI and TortoiseORM makes it 100% async.
|
33
34
|
|
34
35
|
Features:
|
35
36
|
|
@@ -49,7 +50,7 @@ Features:
|
|
49
50
|
**Creating a Project**
|
50
51
|
|
51
52
|
```
|
52
|
-
pip install ohmyapi
|
53
|
+
pip install ohmyapi
|
53
54
|
ohmyapi startproject myproject
|
54
55
|
cd myproject
|
55
56
|
```
|
@@ -119,13 +120,13 @@ router = APIRouter(prefix="/myapp")
|
|
119
120
|
|
120
121
|
@router.get("/")
|
121
122
|
async def list():
|
122
|
-
return await Person.all()
|
123
|
+
return await Person.Schema.many.from_queryset(Person.all())
|
123
124
|
|
124
125
|
|
125
126
|
@router.get("/:id")
|
126
127
|
async def get(id: int):
|
127
128
|
try:
|
128
|
-
await Person.get(pk=id)
|
129
|
+
return await Person.Schema.one(Person.get(pk=id))
|
129
130
|
except DoesNotExist:
|
130
131
|
raise HTTPException(status_code=404, detail="item not found")
|
131
132
|
|
@@ -142,7 +143,21 @@ Similar to Django, first run:
|
|
142
143
|
ohmyapi makemigrations [ <app> ] # no app means all INSTALLED_APPS
|
143
144
|
```
|
144
145
|
|
145
|
-
|
146
|
+
This will create a `migrations/` folder in you project root.
|
147
|
+
|
148
|
+
```
|
149
|
+
myproject/
|
150
|
+
- myapp/
|
151
|
+
- __init__.py
|
152
|
+
- models.py
|
153
|
+
- routes.py
|
154
|
+
- migrations/
|
155
|
+
- myapp/
|
156
|
+
- pyproject.toml
|
157
|
+
- settings.py
|
158
|
+
```
|
159
|
+
|
160
|
+
Apply your migrations via:
|
146
161
|
|
147
162
|
```
|
148
163
|
ohmyapi migrate [ <app> ] # no app means all INSTALLED_APPS
|
@@ -165,6 +180,7 @@ ohmyapi shell
|
|
165
180
|
## Authentication
|
166
181
|
|
167
182
|
A builtin auth app is available.
|
183
|
+
|
168
184
|
Simply add `ohmyapi_auth` to your INSTALLED_APPS and define a JWT_SECRET in your `settings.py`.
|
169
185
|
Remember to `makemigrations` and `migrate` for the auth tables to be created in the database.
|
170
186
|
|
@@ -179,3 +195,10 @@ INSTALLED_APPS = [
|
|
179
195
|
JWT_SECRET = "t0ps3cr3t"
|
180
196
|
```
|
181
197
|
|
198
|
+
Create a super-user:
|
199
|
+
|
200
|
+
```
|
201
|
+
ohmyapi createsuperuser
|
202
|
+
```
|
203
|
+
|
204
|
+
|
@@ -1,6 +1,6 @@
|
|
1
1
|
ohmyapi/__init__.py,sha256=UmLNQImTbKvHEgwQB2Wsyl6fq88X92imL9QZYJpQX4I,18
|
2
2
|
ohmyapi/builtin/auth/__init__.py,sha256=TY1RKgwWmJ6FKz_v4J3m0Ang69qSmtVDLe4rqjLk4-E,69
|
3
|
-
ohmyapi/builtin/auth/models.py,sha256=
|
3
|
+
ohmyapi/builtin/auth/models.py,sha256=PWDUFDzfu6Sl6G6ZujVN7KGBzk6RNUk-moS-g-vEPOM,1490
|
4
4
|
ohmyapi/builtin/auth/permissions.py,sha256=gPBf01UNgXjU3v3DwMSYpKjayZSnwksp-ji4C99oX_I,111
|
5
5
|
ohmyapi/builtin/auth/routes.py,sha256=5jLq92z4TJw4eyiIJ5t7MwRNtYlpN7suDwW28JeEQYA,4902
|
6
6
|
ohmyapi/cli.py,sha256=DaSf1uYxv1bD1XBwVN_rn4F7hq9AlvXwtWzbVw7WPBw,3472
|
@@ -15,9 +15,9 @@ ohmyapi/core/templates/project/settings.py.j2,sha256=c8hHo5DHxNLtam_8Sk8bLYxrjhg
|
|
15
15
|
ohmyapi/db/__init__.py,sha256=p5oXqYUKT6eAoE-9JD-p6zKo9s-H84rJbrKxFna8Kvc,63
|
16
16
|
ohmyapi/db/migration_manager.py,sha256=dTabeDyd6ZIa0lORlKRuiLnBTcsVAgUjgrO9_bew6pQ,3621
|
17
17
|
ohmyapi/db/model/__init__.py,sha256=owKYpx5a2ZFTBgBqyEKN09AvY1cSP0dy1O7d3_sUVgM,33
|
18
|
-
ohmyapi/db/model/model.py,sha256=
|
18
|
+
ohmyapi/db/model/model.py,sha256=PSzK_d8IZQExSRuTrEyPnqfa4GENQhbBZsVxxLWTR4g,1570
|
19
19
|
ohmyapi/router.py,sha256=RzDXrtJJrk4eWdt9qoKBfz6g0S5WZq2hDQoFy5whR6A,40
|
20
|
-
ohmyapi-0.1.
|
21
|
-
ohmyapi-0.1.
|
22
|
-
ohmyapi-0.1.
|
23
|
-
ohmyapi-0.1.
|
20
|
+
ohmyapi-0.1.2.dist-info/METADATA,sha256=nhmQttbrHXueUVFQx8Plu0NvJ2q2BmSrbgWbDwOi-wA,4280
|
21
|
+
ohmyapi-0.1.2.dist-info/WHEEL,sha256=M5asmiAlL6HEcOq52Yi5mmk9KmTVjY2RDPtO4p9DMrc,88
|
22
|
+
ohmyapi-0.1.2.dist-info/entry_points.txt,sha256=ZCZJLw_ojguyr1WOmb8UwHrr42hiLj6-Kzh4mMa_7Ns,44
|
23
|
+
ohmyapi-0.1.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|