muffin-rest 11.0.0__py3-none-any.whl → 11.0.3__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.
- muffin_rest/__init__.py +9 -9
- muffin_rest/filters.py +4 -4
- muffin_rest/handler.py +7 -5
- muffin_rest/mongo/__init__.py +5 -2
- muffin_rest/mongo/utils.py +4 -4
- muffin_rest/openapi.py +2 -1
- muffin_rest/peewee/filters.py +6 -3
- muffin_rest/peewee/handler.py +7 -7
- muffin_rest/peewee/schemas.py +0 -2
- muffin_rest/peewee/sorting.py +2 -2
- muffin_rest/sorting.py +4 -3
- muffin_rest/sqlalchemy/__init__.py +5 -5
- muffin_rest/sqlalchemy/filters.py +2 -2
- muffin_rest/sqlalchemy/sorting.py +9 -4
- muffin_rest-11.0.3.dist-info/METADATA +176 -0
- {muffin_rest-11.0.0.dist-info → muffin_rest-11.0.3.dist-info}/RECORD +18 -18
- muffin_rest-11.0.0.dist-info/METADATA +0 -182
- {muffin_rest-11.0.0.dist-info → muffin_rest-11.0.3.dist-info}/LICENSE +0 -0
- {muffin_rest-11.0.0.dist-info → muffin_rest-11.0.3.dist-info}/WHEEL +0 -0
muffin_rest/__init__.py
CHANGED
|
@@ -17,24 +17,24 @@ Api = API
|
|
|
17
17
|
|
|
18
18
|
__all__ = (
|
|
19
19
|
"API",
|
|
20
|
-
"Api",
|
|
21
|
-
"RESTHandler",
|
|
22
20
|
"APIError",
|
|
23
|
-
"
|
|
21
|
+
"Api",
|
|
22
|
+
"MongoFilter",
|
|
23
|
+
"MongoFilters",
|
|
24
|
+
"MongoRESTHandler",
|
|
25
|
+
"MongoSort",
|
|
26
|
+
"MongoSorting",
|
|
24
27
|
"PWFilter",
|
|
25
28
|
"PWFilters",
|
|
29
|
+
"PWRESTHandler",
|
|
26
30
|
"PWSort",
|
|
27
31
|
"PWSorting",
|
|
28
|
-
"
|
|
32
|
+
"RESTHandler",
|
|
29
33
|
"SAFilter",
|
|
30
34
|
"SAFilters",
|
|
35
|
+
"SARESTHandler",
|
|
31
36
|
"SASort",
|
|
32
37
|
"SASorting",
|
|
33
|
-
"MongoRESTHandler",
|
|
34
|
-
"MongoFilter",
|
|
35
|
-
"MongoFilters",
|
|
36
|
-
"MongoSort",
|
|
37
|
-
"MongoSorting",
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
# Support Peewee ORM
|
muffin_rest/filters.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import operator
|
|
6
|
-
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Iterable, Mapping
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Iterable, Mapping
|
|
7
7
|
|
|
8
8
|
import marshmallow as ma
|
|
9
9
|
from asgi_tools._compat import json_loads
|
|
@@ -59,8 +59,8 @@ class Filter(Mutate):
|
|
|
59
59
|
name: str,
|
|
60
60
|
*,
|
|
61
61
|
field: Any = None,
|
|
62
|
-
schema_field:
|
|
63
|
-
operator:
|
|
62
|
+
schema_field: ma.fields.Field | None = None,
|
|
63
|
+
operator: str | None = None,
|
|
64
64
|
**meta,
|
|
65
65
|
):
|
|
66
66
|
"""Initialize filter.
|
|
@@ -75,7 +75,7 @@ class Filter(Mutate):
|
|
|
75
75
|
self.schema_field = schema_field or self.schema_field
|
|
76
76
|
self.default_operator = operator or self.default_operator
|
|
77
77
|
|
|
78
|
-
async def apply(self, collection: Any, data:
|
|
78
|
+
async def apply(self, collection: Any, data: Mapping | None = None):
|
|
79
79
|
"""Filter given collection."""
|
|
80
80
|
if not data:
|
|
81
81
|
return None, collection
|
muffin_rest/handler.py
CHANGED
|
@@ -39,7 +39,7 @@ class RESTHandlerMeta(HandlerMeta):
|
|
|
39
39
|
|
|
40
40
|
def __new__(mcs, name, bases, params):
|
|
41
41
|
"""Prepare options for the handler."""
|
|
42
|
-
kls = cast(type[
|
|
42
|
+
kls = cast("type[RESTBase]", super().__new__(mcs, name, bases, params))
|
|
43
43
|
kls.meta = kls.meta_class(kls)
|
|
44
44
|
|
|
45
45
|
if getattr(kls.meta, kls.meta_class.base_property, None) is not None:
|
|
@@ -237,8 +237,10 @@ class RESTBase(Generic[TVResource], Handler, metaclass=RESTHandlerMeta):
|
|
|
237
237
|
) -> TVData[TVResource]:
|
|
238
238
|
"""Load data from request and create/update a resource."""
|
|
239
239
|
schema = self.get_schema(request, resource=resource, **schema_options)
|
|
240
|
-
data = cast(Union[Mapping, list], await self.load_data(request))
|
|
241
|
-
return cast(
|
|
240
|
+
data = cast("Union[Mapping, list]", await self.load_data(request))
|
|
241
|
+
return cast(
|
|
242
|
+
"TVData[TVResource]", await load_data(data, schema, partial=resource is not None)
|
|
243
|
+
)
|
|
242
244
|
|
|
243
245
|
@overload
|
|
244
246
|
async def dump( # type: ignore[misc]
|
|
@@ -269,7 +271,7 @@ class RESTBase(Generic[TVResource], Handler, metaclass=RESTHandlerMeta):
|
|
|
269
271
|
if resource
|
|
270
272
|
else self.dump(request, data=self.collection, many=True)
|
|
271
273
|
)
|
|
272
|
-
return ResponseJSON(res)
|
|
274
|
+
return ResponseJSON(res) # type: ignore[type-var]
|
|
273
275
|
|
|
274
276
|
async def post(
|
|
275
277
|
self, request: Request, *, resource: Optional[TVResource] = None
|
|
@@ -283,7 +285,7 @@ class RESTBase(Generic[TVResource], Handler, metaclass=RESTHandlerMeta):
|
|
|
283
285
|
if many:
|
|
284
286
|
data = await self.save_many(request, data, update=resource is not None)
|
|
285
287
|
else:
|
|
286
|
-
data = await self.save(request, cast(TVResource, data), update=resource is not None)
|
|
288
|
+
data = await self.save(request, cast("TVResource", data), update=resource is not None)
|
|
287
289
|
|
|
288
290
|
res = await self.dump(request, data, many=many)
|
|
289
291
|
return ResponseJSON(res)
|
muffin_rest/mongo/__init__.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Mongo DB support."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
from typing import TYPE_CHECKING, Optional, cast
|
|
@@ -68,7 +69,7 @@ class MongoRESTHandler(RESTHandler[TVResource]):
|
|
|
68
69
|
counts = list(self.collection.aggregate(pipeline_num))
|
|
69
70
|
return (
|
|
70
71
|
self.collection.aggregate(pipeline_all),
|
|
71
|
-
counts and counts[0]["total"] or 0, # type: ignore[]
|
|
72
|
+
(counts and counts[0]["total"]) or 0, # type: ignore[]
|
|
72
73
|
)
|
|
73
74
|
total = None
|
|
74
75
|
if self.meta.limit_total:
|
|
@@ -118,7 +119,9 @@ class MongoRESTHandler(RESTHandler[TVResource]):
|
|
|
118
119
|
async def delete(self, request: Request, resource: Optional[TVResource] = None):
|
|
119
120
|
"""Remove the given resource(s)."""
|
|
120
121
|
meta = self.meta
|
|
121
|
-
oids =
|
|
122
|
+
oids = (
|
|
123
|
+
[resource[meta.collection_id]] if resource else cast("list[str]", await request.data())
|
|
124
|
+
)
|
|
122
125
|
if not oids:
|
|
123
126
|
raise APIError.NOT_FOUND()
|
|
124
127
|
|
muffin_rest/mongo/utils.py
CHANGED
|
@@ -73,12 +73,12 @@ class MongoChain:
|
|
|
73
73
|
) -> Awaitable:
|
|
74
74
|
"""Apply filters and return cursor."""
|
|
75
75
|
query = self.__update__(query)
|
|
76
|
-
query = query and {"$and": query} or {}
|
|
76
|
+
query = (query and {"$and": query}) or {}
|
|
77
77
|
return self.collection.find_one(query, projection=projection)
|
|
78
78
|
|
|
79
79
|
def count(self) -> Awaitable[int]:
|
|
80
80
|
"""Count documents."""
|
|
81
|
-
query = self.query and {"$and": self.query} or {}
|
|
81
|
+
query = (self.query and {"$and": self.query}) or {}
|
|
82
82
|
return self.collection.count_documents(query)
|
|
83
83
|
|
|
84
84
|
def aggregate(self, pipeline, **kwargs):
|
|
@@ -121,7 +121,7 @@ class MongoChain:
|
|
|
121
121
|
|
|
122
122
|
def __iter__(self):
|
|
123
123
|
"""Iterate by self collection."""
|
|
124
|
-
query = self.query and {"$and": self.query} or {}
|
|
124
|
+
query = (self.query and {"$and": self.query}) or {}
|
|
125
125
|
if self.sorting:
|
|
126
126
|
return self.collection.find(query, self.projection).sort(self.sorting)
|
|
127
127
|
|
|
@@ -130,7 +130,7 @@ class MongoChain:
|
|
|
130
130
|
def __getattr__(self, name):
|
|
131
131
|
"""Proxy any attributes except find to self.collection."""
|
|
132
132
|
if name in self.CURSOR_METHODS:
|
|
133
|
-
query = self.query and {"$and": self.query} or {}
|
|
133
|
+
query = (self.query and {"$and": self.query}) or {}
|
|
134
134
|
cursor = self.collection.find(query, self.projection)
|
|
135
135
|
if self.sorting:
|
|
136
136
|
cursor = cursor.sort(self.sorting)
|
muffin_rest/openapi.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Create openapi schema from the given API."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
import inspect
|
|
@@ -88,7 +89,7 @@ def route_to_spec(route: Route, spec: APISpec, tags: dict) -> dict:
|
|
|
88
89
|
for param in route.params:
|
|
89
90
|
results["parameters"].append({"in": "path", "name": param})
|
|
90
91
|
|
|
91
|
-
target = cast(Callable, route.target)
|
|
92
|
+
target = cast("Callable", route.target)
|
|
92
93
|
if isinstance(target, partial):
|
|
93
94
|
target = target.func
|
|
94
95
|
|
muffin_rest/peewee/filters.py
CHANGED
|
@@ -15,6 +15,8 @@ from .utils import get_model_field_by_name
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
16
|
from muffin_rest.types import TFilterValue
|
|
17
17
|
|
|
18
|
+
from . import PWRESTHandler
|
|
19
|
+
|
|
18
20
|
|
|
19
21
|
class PWFilter(Filter):
|
|
20
22
|
"""Support Peewee."""
|
|
@@ -41,7 +43,9 @@ class PWFilter(Filter):
|
|
|
41
43
|
"""Apply the filters to Peewee QuerySet.."""
|
|
42
44
|
column = self.field
|
|
43
45
|
if isinstance(column, ColumnBase):
|
|
44
|
-
collection = cast(
|
|
46
|
+
collection = cast(
|
|
47
|
+
"ModelSelect", collection.where(*[op(column, val) for op, val in ops])
|
|
48
|
+
)
|
|
45
49
|
return collection
|
|
46
50
|
|
|
47
51
|
|
|
@@ -52,9 +56,8 @@ class PWFilters(Filters):
|
|
|
52
56
|
|
|
53
57
|
def convert(self, obj: Union[str, Field, PWFilter], **meta):
|
|
54
58
|
"""Convert params to filters."""
|
|
55
|
-
from . import PWRESTHandler
|
|
56
59
|
|
|
57
|
-
handler = cast(PWRESTHandler, self.handler)
|
|
60
|
+
handler = cast("PWRESTHandler", self.handler)
|
|
58
61
|
if isinstance(obj, PWFilter):
|
|
59
62
|
return obj
|
|
60
63
|
|
muffin_rest/peewee/handler.py
CHANGED
|
@@ -5,7 +5,6 @@ from __future__ import annotations
|
|
|
5
5
|
from typing import TYPE_CHECKING, Any, Optional, Union, cast, overload
|
|
6
6
|
|
|
7
7
|
import marshmallow as ma
|
|
8
|
-
import peewee as pw
|
|
9
8
|
from apispec.ext.marshmallow import MarshmallowPlugin
|
|
10
9
|
from marshmallow_peewee import ForeignKey
|
|
11
10
|
from peewee_aio.model import AIOModel, AIOModelSelect
|
|
@@ -19,6 +18,7 @@ from .schemas import EnumField
|
|
|
19
18
|
from .types import TVModel
|
|
20
19
|
|
|
21
20
|
if TYPE_CHECKING:
|
|
21
|
+
import peewee as pw
|
|
22
22
|
from muffin import Request
|
|
23
23
|
from peewee_aio.types import TVAIOModel
|
|
24
24
|
|
|
@@ -52,7 +52,7 @@ class PWRESTBase(RESTBase[TVModel], PeeweeOpenAPIMixin):
|
|
|
52
52
|
) -> pw.ModelSelect: ...
|
|
53
53
|
|
|
54
54
|
# NOTE: there is not a default sorting for peewee (conflict with muffin-admin)
|
|
55
|
-
async def prepare_collection(self, _: Request):
|
|
55
|
+
async def prepare_collection(self, _: Request): # type: ignore[override]
|
|
56
56
|
"""Initialize Peeewee QuerySet for a binded to the resource model."""
|
|
57
57
|
return self.meta.model.select()
|
|
58
58
|
|
|
@@ -86,10 +86,10 @@ class PWRESTBase(RESTBase[TVModel], PeeweeOpenAPIMixin):
|
|
|
86
86
|
self: PWRESTBase[pw.Model], _: Request, *, limit: int = 0, offset: int = 0
|
|
87
87
|
) -> tuple[pw.ModelSelect, int | None]: ...
|
|
88
88
|
|
|
89
|
-
async def paginate(self, _: Request, *, limit: int = 0, offset: int = 0):
|
|
89
|
+
async def paginate(self, _: Request, *, limit: int = 0, offset: int = 0): # type: ignore[override]
|
|
90
90
|
"""Paginate the collection."""
|
|
91
91
|
if self.meta.limit_total:
|
|
92
|
-
cqs = cast(pw.ModelSelect, self.collection.order_by())
|
|
92
|
+
cqs = cast("pw.ModelSelect", self.collection.order_by())
|
|
93
93
|
if cqs._group_by: # type: ignore[misc]
|
|
94
94
|
cqs._returning = cqs._group_by # type: ignore[misc]
|
|
95
95
|
cqs._having = None # type: ignore[misc]
|
|
@@ -114,9 +114,9 @@ class PWRESTBase(RESTBase[TVModel], PeeweeOpenAPIMixin):
|
|
|
114
114
|
meta = self.meta
|
|
115
115
|
manager = meta.manager
|
|
116
116
|
if issubclass(meta.model, AIOModel):
|
|
117
|
-
await resource.save()
|
|
117
|
+
await resource.save(force_insert=not update)
|
|
118
118
|
else:
|
|
119
|
-
await manager.save(resource)
|
|
119
|
+
await manager.save(resource, force_insert=not update)
|
|
120
120
|
|
|
121
121
|
return resource
|
|
122
122
|
|
|
@@ -131,7 +131,7 @@ class PWRESTBase(RESTBase[TVModel], PeeweeOpenAPIMixin):
|
|
|
131
131
|
if not data:
|
|
132
132
|
return
|
|
133
133
|
|
|
134
|
-
model_pk = cast(pw.Field, meta.model_pk)
|
|
134
|
+
model_pk = cast("pw.Field", meta.model_pk)
|
|
135
135
|
resources = await meta.manager.fetchall(self.collection.where(model_pk << data)) # type: ignore[]
|
|
136
136
|
|
|
137
137
|
if not resources:
|
muffin_rest/peewee/schemas.py
CHANGED
muffin_rest/peewee/sorting.py
CHANGED
|
@@ -11,6 +11,7 @@ from muffin_rest.sorting import Sort, Sorting
|
|
|
11
11
|
from .utils import get_model_field_by_name
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
|
+
from . import PWRESTHandler
|
|
14
15
|
from .types import TVCollection
|
|
15
16
|
|
|
16
17
|
|
|
@@ -36,12 +37,11 @@ class PWSorting(Sorting):
|
|
|
36
37
|
|
|
37
38
|
def convert(self, obj: Union[str, Field, PWSort], **meta):
|
|
38
39
|
"""Prepare sorters."""
|
|
39
|
-
from . import PWRESTHandler
|
|
40
40
|
|
|
41
41
|
if isinstance(obj, PWSort):
|
|
42
42
|
return obj
|
|
43
43
|
|
|
44
|
-
handler = cast(PWRESTHandler, self.handler)
|
|
44
|
+
handler = cast("PWRESTHandler", self.handler)
|
|
45
45
|
|
|
46
46
|
if isinstance(obj, Field):
|
|
47
47
|
name, field = obj.name, obj
|
muffin_rest/sorting.py
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
"""Implement sorting."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
from typing import TYPE_CHECKING, Any, Generator, Iterable, Mapping, Sequence, cast
|
|
5
6
|
|
|
6
|
-
from .types import TVCollection
|
|
7
7
|
from .utils import Mutate, Mutator
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from muffin import Request
|
|
11
11
|
|
|
12
12
|
from .handler import RESTBase
|
|
13
|
+
from .types import TVCollection
|
|
13
14
|
|
|
14
15
|
SORT_PARAM = "sort"
|
|
15
16
|
|
|
@@ -60,14 +61,14 @@ class Sorting(Mutator):
|
|
|
60
61
|
|
|
61
62
|
def convert(self, obj, **meta) -> Sort:
|
|
62
63
|
"""Prepare sorters."""
|
|
63
|
-
sort = cast(Sort, super(Sorting, self).convert(obj, **meta))
|
|
64
|
+
sort = cast("Sort", super(Sorting, self).convert(obj, **meta))
|
|
64
65
|
if sort.meta.get("default"):
|
|
65
66
|
self.default.append(sort)
|
|
66
67
|
return sort
|
|
67
68
|
|
|
68
69
|
def sort_default(self, collection: TVCollection) -> TVCollection:
|
|
69
70
|
"""Sort by default."""
|
|
70
|
-
return cast(TVCollection, sorted(collection))
|
|
71
|
+
return cast("TVCollection", sorted(collection))
|
|
71
72
|
|
|
72
73
|
@property
|
|
73
74
|
def openapi(self):
|
|
@@ -22,7 +22,7 @@ if TYPE_CHECKING:
|
|
|
22
22
|
|
|
23
23
|
from .types import TVResource
|
|
24
24
|
|
|
25
|
-
ModelConverter._get_field_name = lambda _, prop_or_column: str(prop_or_column.key) # type: ignore[
|
|
25
|
+
ModelConverter._get_field_name = lambda _, prop_or_column: str(prop_or_column.key) # type: ignore[assignment]
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class SQLAlchemyAutoSchema(BaseSQLAlchemyAutoSchema):
|
|
@@ -152,7 +152,7 @@ class SARESTHandler(RESTHandler[TVResource]):
|
|
|
152
152
|
resource = await self.meta.database.fetch_one(qs)
|
|
153
153
|
if resource is None:
|
|
154
154
|
raise APIError.NOT_FOUND("Resource not found")
|
|
155
|
-
return cast(TVResource, dict(resource))
|
|
155
|
+
return cast("TVResource", dict(resource))
|
|
156
156
|
|
|
157
157
|
def get_schema(
|
|
158
158
|
self, request: Request, *, resource: Optional[TVResource] = None, **schema_options
|
|
@@ -164,7 +164,7 @@ class SARESTHandler(RESTHandler[TVResource]):
|
|
|
164
164
|
"""Save the given resource."""
|
|
165
165
|
meta = self.meta
|
|
166
166
|
insert_query = meta.table.insert()
|
|
167
|
-
table_pk = cast(sa.Column, meta.table_pk)
|
|
167
|
+
table_pk = cast("sa.Column", meta.table_pk)
|
|
168
168
|
if update:
|
|
169
169
|
update_query = self.meta.table.update().where(table_pk == resource[table_pk.name]) # type: ignore[call-overload]
|
|
170
170
|
await meta.database.execute(update_query, resource)
|
|
@@ -176,12 +176,12 @@ class SARESTHandler(RESTHandler[TVResource]):
|
|
|
176
176
|
|
|
177
177
|
async def remove(self, request: Request, resource: Optional[TVResource] = None):
|
|
178
178
|
"""Remove the given resource."""
|
|
179
|
-
table_pk = cast(sa.Column, self.meta.table_pk)
|
|
179
|
+
table_pk = cast("sa.Column", self.meta.table_pk)
|
|
180
180
|
pks = [resource[table_pk.name]] if resource else await request.data()
|
|
181
181
|
if not pks:
|
|
182
182
|
raise APIError.NOT_FOUND()
|
|
183
183
|
|
|
184
|
-
delete = self.meta.table.delete().where(table_pk.in_(cast(list[Any], pks)))
|
|
184
|
+
delete = self.meta.table.delete().where(table_pk.in_(cast("list[Any]", pks)))
|
|
185
185
|
await self.meta.database.execute(delete)
|
|
186
186
|
|
|
187
187
|
delete = remove
|
|
@@ -11,6 +11,7 @@ from muffin_rest.filters import Filter, Filters
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
12
|
from muffin_rest.types import TFilterValue
|
|
13
13
|
|
|
14
|
+
from . import SARESTHandler
|
|
14
15
|
from .types import TVCollection
|
|
15
16
|
|
|
16
17
|
|
|
@@ -51,9 +52,8 @@ class SAFilters(Filters):
|
|
|
51
52
|
|
|
52
53
|
def convert(self, obj: Union[str, Column, SAFilter], **meta):
|
|
53
54
|
"""Convert params to filters."""
|
|
54
|
-
from . import SARESTHandler
|
|
55
55
|
|
|
56
|
-
handler = cast(SARESTHandler, self.handler)
|
|
56
|
+
handler = cast("SARESTHandler", self.handler)
|
|
57
57
|
|
|
58
58
|
if isinstance(obj, SAFilter):
|
|
59
59
|
if obj.field is None:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Support sorting for SQLAlchemy ORM."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
from typing import TYPE_CHECKING, Union, cast
|
|
@@ -8,6 +9,7 @@ from sqlalchemy import Column
|
|
|
8
9
|
from muffin_rest.sorting import Sort, Sorting
|
|
9
10
|
|
|
10
11
|
if TYPE_CHECKING:
|
|
12
|
+
from . import SARESTHandler
|
|
11
13
|
from .types import TVCollection
|
|
12
14
|
|
|
13
15
|
|
|
@@ -15,7 +17,11 @@ class SASort(Sort):
|
|
|
15
17
|
"""Sorter for Peewee."""
|
|
16
18
|
|
|
17
19
|
async def apply(
|
|
18
|
-
self,
|
|
20
|
+
self,
|
|
21
|
+
collection: TVCollection,
|
|
22
|
+
*,
|
|
23
|
+
desc: bool = False,
|
|
24
|
+
**_,
|
|
19
25
|
) -> TVCollection:
|
|
20
26
|
"""Sort the collection."""
|
|
21
27
|
field = self.field
|
|
@@ -32,19 +38,18 @@ class SASorting(Sorting):
|
|
|
32
38
|
|
|
33
39
|
def convert(self, obj: Union[str, Column, SASort], **meta):
|
|
34
40
|
"""Prepare sorters."""
|
|
35
|
-
from . import SARESTHandler
|
|
36
41
|
|
|
37
42
|
if isinstance(obj, SASort):
|
|
38
43
|
return obj
|
|
39
44
|
|
|
40
|
-
handler = cast(SARESTHandler, self.handler)
|
|
45
|
+
handler = cast("SARESTHandler", self.handler)
|
|
41
46
|
|
|
42
47
|
if isinstance(obj, Column):
|
|
43
48
|
name, field = obj.name, obj
|
|
44
49
|
|
|
45
50
|
else:
|
|
46
51
|
name = obj
|
|
47
|
-
field = meta.get("field", handler.meta.table.c.get(name))
|
|
52
|
+
field = meta.get("field", handler.meta.table.c.get(name)) # type: ignore[assignment]
|
|
48
53
|
|
|
49
54
|
if field is not None:
|
|
50
55
|
sort = self.MUTATE_CLASS(name, field=field, **meta)
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: muffin-rest
|
|
3
|
+
Version: 11.0.3
|
|
4
|
+
Summary: The package provides enhanced support for writing REST APIs with Muffin framework
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: rest,api,muffin,asgi,asyncio,trio
|
|
7
|
+
Author: Kirill Klenov
|
|
8
|
+
Author-email: horneds@gmail.com
|
|
9
|
+
Requires-Python: >=3.10,<4.0
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Framework :: AsyncIO
|
|
12
|
+
Classifier: Framework :: Trio
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
22
|
+
Provides-Extra: peewee
|
|
23
|
+
Provides-Extra: sqlalchemy
|
|
24
|
+
Provides-Extra: yaml
|
|
25
|
+
Requires-Dist: apispec (>=6,<7)
|
|
26
|
+
Requires-Dist: marshmallow (>=3,<4)
|
|
27
|
+
Requires-Dist: marshmallow-peewee ; extra == "peewee"
|
|
28
|
+
Requires-Dist: marshmallow-sqlalchemy ; extra == "sqlalchemy"
|
|
29
|
+
Requires-Dist: muffin
|
|
30
|
+
Requires-Dist: muffin-databases ; extra == "sqlalchemy"
|
|
31
|
+
Requires-Dist: muffin-peewee-aio ; extra == "peewee"
|
|
32
|
+
Requires-Dist: pyyaml ; extra == "yaml"
|
|
33
|
+
Requires-Dist: sqlalchemy ; extra == "sqlalchemy"
|
|
34
|
+
Project-URL: Homepage, https://github.com/klen/muffin-rest
|
|
35
|
+
Project-URL: Repository, https://github.com/klen/muffin-rest
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# Muffin‑REST
|
|
39
|
+
|
|
40
|
+
**Muffin‑REST** simplifies building RESTful APIs with [Muffin](https://github.com/klen/muffin) by offering:
|
|
41
|
+
|
|
42
|
+
- Declarative `API` class with resource registration
|
|
43
|
+
- Built-in filtering, sorting, pagination, and search
|
|
44
|
+
- Support for:
|
|
45
|
+
- [Peewee ORM](http://docs.peewee-orm.com/en/latest/) via `PeeweeEndpoint`
|
|
46
|
+
- [SQLAlchemy Core](https://docs.sqlalchemy.org/en/14/core/) via `SAEndpoint`
|
|
47
|
+
- [MongoDB](https://www.mongodb.com/) via `MongoEndpoint`
|
|
48
|
+
- [Swagger/OpenAPI](https://swagger.io/) autodocumentation
|
|
49
|
+
- Works with asyncio, Trio, and Curio
|
|
50
|
+
|
|
51
|
+
[](https://github.com/klen/muffin-rest/actions)
|
|
52
|
+
[](https://pypi.org/project/muffin-rest/)
|
|
53
|
+
[](https://pypi.org/project/muffin-rest/)
|
|
54
|
+
|
|
55
|
+
## Requirements
|
|
56
|
+
|
|
57
|
+
- Python >= 3.10
|
|
58
|
+
- Trio requires Peewee backend
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
Install core package:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install muffin-rest
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Add optional backend support:
|
|
69
|
+
|
|
70
|
+
- SQLAlchemy Core: `pip install muffin-rest[sqlalchemy]`
|
|
71
|
+
- Peewee ORM: `pip install muffin-rest[peewee]`
|
|
72
|
+
- YAML support for Swagger: `pip install muffin-rest[yaml]`
|
|
73
|
+
|
|
74
|
+
## Quickstart (Peewee example)
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from muffin import Application
|
|
78
|
+
from muffin_rest import API
|
|
79
|
+
from muffin_rest.peewee import PeeweeEndpoint
|
|
80
|
+
from models import User # your Peewee model
|
|
81
|
+
|
|
82
|
+
app = Application("myapp")
|
|
83
|
+
api = API(title="User Service", version="1.0")
|
|
84
|
+
|
|
85
|
+
@api.route
|
|
86
|
+
class UsersEndpoint(PeeweeEndpoint):
|
|
87
|
+
class Meta:
|
|
88
|
+
model = User
|
|
89
|
+
lookup_field = "id"
|
|
90
|
+
filters = ["name", "email"]
|
|
91
|
+
ordering = ["-created_at"]
|
|
92
|
+
|
|
93
|
+
api.setup(app, prefix="/api", swagger=True)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Endpoints available:
|
|
97
|
+
|
|
98
|
+
- `GET /api/users/` — list with pagination, search, filtering
|
|
99
|
+
- `POST /api/users/` — create
|
|
100
|
+
- `GET /api/users/{id}/` — retrieve
|
|
101
|
+
- `PUT /api/users/{id}/` — replace
|
|
102
|
+
- `PATCH /api/users/{id}/` — update
|
|
103
|
+
- `DELETE /api/users/{id}/` — remove
|
|
104
|
+
- Docs: `/api/docs/`, OpenAPI spec: `/api/openapi.json`
|
|
105
|
+
|
|
106
|
+
## Usage with SQLAlchemy
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from muffin_rest import API
|
|
110
|
+
from muffin_rest.sqlalchemy import SAEndpoint
|
|
111
|
+
from models import my_table, db_engine
|
|
112
|
+
|
|
113
|
+
api = API()
|
|
114
|
+
@api.route
|
|
115
|
+
class MySAEndpoint(SAEndpoint):
|
|
116
|
+
class Meta:
|
|
117
|
+
table = my_table
|
|
118
|
+
database = db_engine
|
|
119
|
+
|
|
120
|
+
api.setup(app)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Usage with MongoDB
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from muffin_rest import API
|
|
127
|
+
from muffin_rest.mongo import MongoEndpoint
|
|
128
|
+
from models import mongo_collection
|
|
129
|
+
|
|
130
|
+
api = API()
|
|
131
|
+
@api.route
|
|
132
|
+
class MyMongoEndpoint(MongoEndpoint):
|
|
133
|
+
class Meta:
|
|
134
|
+
collection = mongo_collection
|
|
135
|
+
|
|
136
|
+
api.setup(app)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Advanced Configuration
|
|
140
|
+
|
|
141
|
+
Customize Swagger and routes via constructor:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
api = API(
|
|
145
|
+
title="Service API",
|
|
146
|
+
version="2.1",
|
|
147
|
+
swagger_ui=True,
|
|
148
|
+
openapi_path="/api/openapi.json",
|
|
149
|
+
docs_path="/api/docs/"
|
|
150
|
+
)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Contributing & Examples
|
|
154
|
+
|
|
155
|
+
- See `examples/` for live application demos
|
|
156
|
+
- Tests in `tests/` focus on filtering, pagination, status codes
|
|
157
|
+
- Check `CHANGELOG.md` for latest changes
|
|
158
|
+
|
|
159
|
+
## Bug Tracker
|
|
160
|
+
|
|
161
|
+
Report bugs or request features:
|
|
162
|
+
https://github.com/klen/muffin-rest/issues
|
|
163
|
+
|
|
164
|
+
## Contributing
|
|
165
|
+
|
|
166
|
+
Repo: https://github.com/klen/muffin-rest
|
|
167
|
+
Pull requests, example additions, docs improvements welcome!
|
|
168
|
+
|
|
169
|
+
## Contributors
|
|
170
|
+
|
|
171
|
+
- [klen](https://github.com/klen) (Kirill Klenov)
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
Licensed under the [MIT license](http://opensource.org/licenses/MIT).
|
|
176
|
+
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
muffin_rest/__init__.py,sha256=
|
|
1
|
+
muffin_rest/__init__.py,sha256=JnwsHQNDmJpOqETeIjjPheG891T5WQ0JBaiEV72Wcmg,1242
|
|
2
2
|
muffin_rest/api.py,sha256=zssoHjqTsa8UCAyxj6TxQVluYPX9Sigyiph6SRxBY6I,3870
|
|
3
3
|
muffin_rest/errors.py,sha256=mxEBhNPo3pwpG2em6zaQonbfRgHFBJ3I8WunVYWDjvM,1163
|
|
4
|
-
muffin_rest/filters.py,sha256=
|
|
5
|
-
muffin_rest/handler.py,sha256=
|
|
4
|
+
muffin_rest/filters.py,sha256=nWlwDLuId-mmP3tgZjp1joYCVx0k9ekV42fPIMMKphM,5920
|
|
5
|
+
muffin_rest/handler.py,sha256=lgZ0KVnzVye8eMZ4qPuo-cSCvg8Vh_fFfTw5g7pJNio,10878
|
|
6
6
|
muffin_rest/limits.py,sha256=Fnlu4Wj3B-BzpahLK-rDbd1GEd7CEQ3zxyOD0vee7GE,2007
|
|
7
7
|
muffin_rest/marshmallow.py,sha256=UeHxZLtETlrheDAlMZn7Xv_7vWPxvJQo-sj05nql9gM,574
|
|
8
|
-
muffin_rest/mongo/__init__.py,sha256=
|
|
8
|
+
muffin_rest/mongo/__init__.py,sha256=AVOM6ydkEZ4CAOefOlpNnDx0dlD65LdhAkLh_VR8Qf4,4685
|
|
9
9
|
muffin_rest/mongo/filters.py,sha256=yIxIDVqMn6SoDgVhCqiTxYetw0hoaf_3jIvX2Vnizok,964
|
|
10
10
|
muffin_rest/mongo/schema.py,sha256=y4OEPQnlV_COTIIQ3cKmpqDpD2r18eAWn0rijQldWm0,1205
|
|
11
11
|
muffin_rest/mongo/sorting.py,sha256=iJBnaFwE7g_JMwpGpQkoqSqbQK9XULx1K3skiRRgLgY,870
|
|
12
12
|
muffin_rest/mongo/types.py,sha256=jaODScgwwYbzHis3DY4bPzU1ahiMJMSwquH5_Thi-Gg,200
|
|
13
|
-
muffin_rest/mongo/utils.py,sha256=
|
|
14
|
-
muffin_rest/openapi.py,sha256=
|
|
13
|
+
muffin_rest/mongo/utils.py,sha256=_6J-FYfU2OIjgFLF9JHRr98vHLzW4ACeL62pBXiuF_M,3966
|
|
14
|
+
muffin_rest/openapi.py,sha256=kpynUbNmK78L-2xTbn13FBgmPWLPGMKF_yejVGfHLwg,8754
|
|
15
15
|
muffin_rest/options.py,sha256=38y7AasyAghXNffxX-3xgqLbWDQ1RxAhg77ze7c0Mu0,2232
|
|
16
16
|
muffin_rest/peewee/__init__.py,sha256=94DSj_ftT6fbPksHlBv40AH2HWaiZommUFOMN2jd9a4,129
|
|
17
|
-
muffin_rest/peewee/filters.py,sha256=
|
|
18
|
-
muffin_rest/peewee/handler.py,sha256=
|
|
17
|
+
muffin_rest/peewee/filters.py,sha256=BL6Zd8TI7wc7ZacZJTPVmTPfsKk6v8Z634HA5t798c0,2506
|
|
18
|
+
muffin_rest/peewee/handler.py,sha256=qiJNZRj-7wHO9Fdkt8uV_lPmWJtBvjZQL-I6z5_BvXQ,5469
|
|
19
19
|
muffin_rest/peewee/openapi.py,sha256=lDnLnoXi33p0YeFVwRgaVrndyrG2XL93RH-BzbxinOY,1105
|
|
20
20
|
muffin_rest/peewee/options.py,sha256=TimJtErC9e8B7BRiEkHiBZd71_bZbYr-FE2PIlQvfH0,1455
|
|
21
|
-
muffin_rest/peewee/schemas.py,sha256=
|
|
22
|
-
muffin_rest/peewee/sorting.py,sha256=
|
|
21
|
+
muffin_rest/peewee/schemas.py,sha256=hQG2UKR7gEMsz0n75L3tvkVzUX3baXMJ94emgIc1Scw,439
|
|
22
|
+
muffin_rest/peewee/sorting.py,sha256=3IQzA1UfMRXl6NAp1yUyTv9-pNSkObtwI1-oZdbetQc,1975
|
|
23
23
|
muffin_rest/peewee/types.py,sha256=cgCXhpGHkImKwudA1lulZHz5oJswHH168AiW5MhZRCM,155
|
|
24
24
|
muffin_rest/peewee/utils.py,sha256=wrDcEEeCMzK4GdMSBi7BX0XpBPRZ6pF_EHwDsZPgL1s,706
|
|
25
25
|
muffin_rest/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
26
|
muffin_rest/redoc.html,sha256=GtuHIMvTuSi8Ro6bgI-G8VB94AljMyfjcZseqtBmGCY,559
|
|
27
27
|
muffin_rest/schemas.py,sha256=BW3dF82C6Q6STs4tZjej1x8Ii1rI3EZUJZR4mNNKmu4,875
|
|
28
|
-
muffin_rest/sorting.py,sha256=
|
|
29
|
-
muffin_rest/sqlalchemy/__init__.py,sha256=
|
|
30
|
-
muffin_rest/sqlalchemy/filters.py,sha256=
|
|
31
|
-
muffin_rest/sqlalchemy/sorting.py,sha256=
|
|
28
|
+
muffin_rest/sorting.py,sha256=nu4SGW5uxLETJIywE-1UTCCya9QAvYWcq-P8dPWQixI,2822
|
|
29
|
+
muffin_rest/sqlalchemy/__init__.py,sha256=pO6flB9vg3neaEZLp2hFBte2M0ImrssG1gQLjuuqg_4,6398
|
|
30
|
+
muffin_rest/sqlalchemy/filters.py,sha256=hmjvxsJF0ddePgrLZ1DPmQ8vd5QwPTTlza3xOZZHquk,2487
|
|
31
|
+
muffin_rest/sqlalchemy/sorting.py,sha256=OcuNdgd2__AWXJhlfykqIyTOqAKpyZ3WrK_acNT_t6c,1702
|
|
32
32
|
muffin_rest/sqlalchemy/types.py,sha256=Exm-zAQCtPAwXvYcCTtPRqSa-wTEWRcH_v2YSsJkB6s,198
|
|
33
33
|
muffin_rest/swagger.html,sha256=2uGLu_KpkYf925KnDKHBJmV9pm6OHn5C3BWScESsUS8,1736
|
|
34
34
|
muffin_rest/types.py,sha256=m27-g6BI7qdSWGym4fWALBJa2ZpWR0_m0nlrDx7iTCo,566
|
|
35
35
|
muffin_rest/utils.py,sha256=c08E4HJ4SLYC-91GKPEbsyKTZ4sZbTN4qDqJbNg_HTE,2076
|
|
36
|
-
muffin_rest-11.0.
|
|
37
|
-
muffin_rest-11.0.
|
|
38
|
-
muffin_rest-11.0.
|
|
39
|
-
muffin_rest-11.0.
|
|
36
|
+
muffin_rest-11.0.3.dist-info/LICENSE,sha256=xHPkOZhjyKBMOwXpWn9IB_BVLjrrMxv2M9slKkHj2hM,1082
|
|
37
|
+
muffin_rest-11.0.3.dist-info/METADATA,sha256=AOTpoLl7eDiaxX86rrwNlfeEFP6t6tDsAW02FBxzgMU,4912
|
|
38
|
+
muffin_rest-11.0.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
39
|
+
muffin_rest-11.0.3.dist-info/RECORD,,
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: muffin-rest
|
|
3
|
-
Version: 11.0.0
|
|
4
|
-
Summary: The package provides enhanced support for writing REST APIs with Muffin framework
|
|
5
|
-
License: MIT
|
|
6
|
-
Keywords: rest,api,muffin,asgi,asyncio,trio
|
|
7
|
-
Author: Kirill Klenov
|
|
8
|
-
Author-email: horneds@gmail.com
|
|
9
|
-
Requires-Python: >=3.9,<4.0
|
|
10
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
-
Classifier: Framework :: AsyncIO
|
|
12
|
-
Classifier: Framework :: Trio
|
|
13
|
-
Classifier: Intended Audience :: Developers
|
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
-
Classifier: Programming Language :: Python
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
-
Classifier: Topic :: Internet :: WWW/HTTP
|
|
23
|
-
Provides-Extra: peewee
|
|
24
|
-
Provides-Extra: sqlalchemy
|
|
25
|
-
Provides-Extra: yaml
|
|
26
|
-
Requires-Dist: apispec (>=6,<7)
|
|
27
|
-
Requires-Dist: marshmallow (>=3,<4)
|
|
28
|
-
Requires-Dist: marshmallow-peewee ; extra == "peewee"
|
|
29
|
-
Requires-Dist: marshmallow-sqlalchemy ; extra == "sqlalchemy"
|
|
30
|
-
Requires-Dist: muffin (>=0,<1)
|
|
31
|
-
Requires-Dist: muffin-databases ; extra == "sqlalchemy"
|
|
32
|
-
Requires-Dist: muffin-peewee-aio ; extra == "peewee"
|
|
33
|
-
Requires-Dist: pyyaml ; extra == "yaml"
|
|
34
|
-
Requires-Dist: sqlalchemy ; extra == "sqlalchemy"
|
|
35
|
-
Project-URL: Homepage, https://github.com/klen/muffin-rest
|
|
36
|
-
Project-URL: Repository, https://github.com/klen/muffin-rest
|
|
37
|
-
Description-Content-Type: text/x-rst
|
|
38
|
-
|
|
39
|
-
Muffin-REST
|
|
40
|
-
###########
|
|
41
|
-
|
|
42
|
-
.. _description:
|
|
43
|
-
|
|
44
|
-
**Muffin-REST** -- provides enhanced support for writing REST APIs with Muffin_.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
.. _badges:
|
|
48
|
-
|
|
49
|
-
.. image:: https://github.com/klen/muffin-rest/workflows/tests/badge.svg
|
|
50
|
-
:target: https://github.com/klen/muffin-rest/actions
|
|
51
|
-
:alt: Tests Status
|
|
52
|
-
|
|
53
|
-
.. image:: https://img.shields.io/pypi/v/muffin-rest
|
|
54
|
-
:target: https://pypi.org/project/muffin-rest/
|
|
55
|
-
:alt: PYPI Version
|
|
56
|
-
|
|
57
|
-
.. image:: https://img.shields.io/pypi/pyversions/muffin-rest
|
|
58
|
-
:target: https://pypi.org/project/muffin-rest/
|
|
59
|
-
:alt: Python Versions
|
|
60
|
-
|
|
61
|
-
----------
|
|
62
|
-
|
|
63
|
-
.. _features:
|
|
64
|
-
|
|
65
|
-
Features
|
|
66
|
-
--------
|
|
67
|
-
|
|
68
|
-
- API class to simplify the creation of REST APIs;
|
|
69
|
-
- Automatic filtering and sorting for resources;
|
|
70
|
-
- Support for `Peewee ORM`_, Mongo_, `SQLAlchemy Core`_;
|
|
71
|
-
- Auto documentation with Swagger_;
|
|
72
|
-
|
|
73
|
-
.. _contents:
|
|
74
|
-
|
|
75
|
-
.. contents::
|
|
76
|
-
|
|
77
|
-
.. _requirements:
|
|
78
|
-
|
|
79
|
-
Requirements
|
|
80
|
-
=============
|
|
81
|
-
|
|
82
|
-
- python >= 3.9
|
|
83
|
-
|
|
84
|
-
.. note:: Trio is only supported with Peewee ORM
|
|
85
|
-
|
|
86
|
-
.. _installation:
|
|
87
|
-
|
|
88
|
-
Installation
|
|
89
|
-
=============
|
|
90
|
-
|
|
91
|
-
**Muffin-REST** should be installed using pip: ::
|
|
92
|
-
|
|
93
|
-
pip install muffin-rest
|
|
94
|
-
|
|
95
|
-
With `SQLAlchemy Core`_ support: ::
|
|
96
|
-
|
|
97
|
-
pip install muffin-rest[sqlalchemy]
|
|
98
|
-
|
|
99
|
-
With `Peewee ORM`_ support: ::
|
|
100
|
-
|
|
101
|
-
pip install muffin-rest[peewee]
|
|
102
|
-
|
|
103
|
-
With YAML support for autodocumentation: ::
|
|
104
|
-
|
|
105
|
-
pip install muffin-rest[yaml]
|
|
106
|
-
|
|
107
|
-
.. _usage:
|
|
108
|
-
|
|
109
|
-
Usage
|
|
110
|
-
=====
|
|
111
|
-
|
|
112
|
-
Create an API:
|
|
113
|
-
|
|
114
|
-
.. code-block:: python
|
|
115
|
-
|
|
116
|
-
from muffin_rest import API
|
|
117
|
-
|
|
118
|
-
api = API()
|
|
119
|
-
|
|
120
|
-
Create endpoints and connect them to the API (example for sqlalchemy):
|
|
121
|
-
|
|
122
|
-
.. code-block:: python
|
|
123
|
-
|
|
124
|
-
from muffin_rest.sqlalchemy import SAEndpoint
|
|
125
|
-
from project.api import api
|
|
126
|
-
|
|
127
|
-
@api.route
|
|
128
|
-
class MyEndpoint(SAEndpoint):
|
|
129
|
-
|
|
130
|
-
class Meta:
|
|
131
|
-
table = MyTable
|
|
132
|
-
database = db
|
|
133
|
-
|
|
134
|
-
Connect it to your Muffin_ application:
|
|
135
|
-
|
|
136
|
-
.. code-block:: python
|
|
137
|
-
|
|
138
|
-
from project.api import api
|
|
139
|
-
|
|
140
|
-
api.setup(app, prefix='/api/v1')
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
.. _bugtracker:
|
|
144
|
-
|
|
145
|
-
Bug tracker
|
|
146
|
-
===========
|
|
147
|
-
|
|
148
|
-
If you have any suggestions, bug reports or
|
|
149
|
-
annoyances please report them to the issue tracker
|
|
150
|
-
at https://github.com/klen/muffin-rest/issues
|
|
151
|
-
|
|
152
|
-
.. _contributing:
|
|
153
|
-
|
|
154
|
-
Contributing
|
|
155
|
-
============
|
|
156
|
-
|
|
157
|
-
Development of Muffin-REST happens at: https://github.com/klen/muffin-rest
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
Contributors
|
|
161
|
-
=============
|
|
162
|
-
|
|
163
|
-
* klen_ (Kirill Klenov)
|
|
164
|
-
|
|
165
|
-
.. _license:
|
|
166
|
-
|
|
167
|
-
License
|
|
168
|
-
========
|
|
169
|
-
|
|
170
|
-
Licensed under a `MIT license`_.
|
|
171
|
-
|
|
172
|
-
.. _links:
|
|
173
|
-
|
|
174
|
-
.. _klen: https://github.com/klen
|
|
175
|
-
.. _Muffin: https://github.com/klen/muffin
|
|
176
|
-
.. _Swagger: https://swagger.io/tools/swagger-ui/
|
|
177
|
-
.. _Mongo: https://www.mongodb.com/
|
|
178
|
-
.. _Peewee ORM: http://docs.peewee-orm.com/en/latest/
|
|
179
|
-
.. _SqlAlchemy Core: https://docs.sqlalchemy.org/en/14/core/
|
|
180
|
-
|
|
181
|
-
.. _MIT license: http://opensource.org/licenses/MIT
|
|
182
|
-
|
|
File without changes
|
|
File without changes
|