panther 4.1.2__py3-none-any.whl → 4.1.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.
- panther/__init__.py +1 -1
- panther/app.py +11 -5
- panther/db/models.py +1 -1
- panther/db/queries/mongodb_queries.py +24 -9
- panther/db/queries/queries.py +7 -3
- panther/file_handler.py +18 -1
- panther/generics.py +23 -4
- panther/serializer.py +2 -2
- {panther-4.1.2.dist-info → panther-4.1.3.dist-info}/METADATA +1 -1
- {panther-4.1.2.dist-info → panther-4.1.3.dist-info}/RECORD +14 -14
- {panther-4.1.2.dist-info → panther-4.1.3.dist-info}/LICENSE +0 -0
- {panther-4.1.2.dist-info → panther-4.1.3.dist-info}/WHEEL +0 -0
- {panther-4.1.2.dist-info → panther-4.1.3.dist-info}/entry_points.txt +0 -0
- {panther-4.1.2.dist-info → panther-4.1.3.dist-info}/top_level.txt +0 -0
panther/__init__.py
CHANGED
panther/app.py
CHANGED
@@ -151,15 +151,15 @@ class API:
|
|
151
151
|
# `request` will be ignored in regular `BaseModel`
|
152
152
|
return model(**request.data, request=request)
|
153
153
|
except ValidationError as validation_error:
|
154
|
-
error = {'.'.join(loc for loc in e['loc']): e['msg'] for e in validation_error.errors()}
|
154
|
+
error = {'.'.join(str(loc) for loc in e['loc']): e['msg'] for e in validation_error.errors()}
|
155
155
|
raise BadRequestAPIError(detail=error)
|
156
156
|
except JSONDecodeError:
|
157
157
|
raise JSONDecodeAPIError
|
158
158
|
|
159
159
|
|
160
160
|
class GenericAPI:
|
161
|
-
input_model: type[ModelSerializer] | type[BaseModel] = None
|
162
|
-
output_model: type[ModelSerializer] | type[BaseModel] = None
|
161
|
+
input_model: type[ModelSerializer] | type[BaseModel] | None = None
|
162
|
+
output_model: type[ModelSerializer] | type[BaseModel] | None = None
|
163
163
|
auth: bool = False
|
164
164
|
permissions: list | None = None
|
165
165
|
throttling: Throttling | None = None
|
@@ -181,6 +181,12 @@ class GenericAPI:
|
|
181
181
|
async def delete(self, *args, **kwargs):
|
182
182
|
raise MethodNotAllowedAPIError
|
183
183
|
|
184
|
+
async def get_input_model(self, request: Request) -> type[ModelSerializer] | type[BaseModel] | None:
|
185
|
+
return None
|
186
|
+
|
187
|
+
async def get_output_model(self, request: Request) -> type[ModelSerializer] | type[BaseModel] | None:
|
188
|
+
return None
|
189
|
+
|
184
190
|
async def call_method(self, request: Request):
|
185
191
|
match request.method:
|
186
192
|
case 'GET':
|
@@ -197,8 +203,8 @@ class GenericAPI:
|
|
197
203
|
raise MethodNotAllowedAPIError
|
198
204
|
|
199
205
|
return await API(
|
200
|
-
input_model=self.input_model,
|
201
|
-
output_model=self.output_model,
|
206
|
+
input_model=self.input_model or await self.get_input_model(request=request),
|
207
|
+
output_model=self.output_model or await self.get_output_model(request=request),
|
202
208
|
auth=self.auth,
|
203
209
|
permissions=self.permissions,
|
204
210
|
throttling=self.throttling,
|
panther/db/models.py
CHANGED
@@ -102,21 +102,36 @@ class BaseMongoDBQuery(BaseQuery):
|
|
102
102
|
|
103
103
|
# # # # # Update # # # # #
|
104
104
|
async def update(self, _update: dict | None = None, /, **kwargs) -> None:
|
105
|
-
|
106
|
-
|
107
|
-
self._validate_data(data=document, is_updating=True)
|
105
|
+
merged_update_query = self._merge(_update, kwargs)
|
106
|
+
merged_update_query.pop('_id', None)
|
108
107
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
108
|
+
self._validate_data(data=merged_update_query, is_updating=True)
|
109
|
+
|
110
|
+
update_query = {}
|
111
|
+
for field, value in merged_update_query.items():
|
112
|
+
if field.startswith('$'):
|
113
|
+
update_query[field] = value
|
114
|
+
else:
|
115
|
+
update_query['$set'] = update_query.get('$set', {})
|
116
|
+
update_query['$set'][field] = value
|
117
|
+
setattr(self, field, value)
|
118
|
+
|
119
|
+
await db.session[self.__class__.__name__].update_one({'_id': self._id}, update_query)
|
113
120
|
|
114
121
|
@classmethod
|
115
122
|
async def update_one(cls, _filter: dict, _update: dict | None = None, /, **kwargs) -> bool:
|
116
123
|
prepare_id_for_query(_filter, is_mongo=True)
|
117
|
-
|
124
|
+
merged_update_query = cls._merge(_update, kwargs)
|
125
|
+
|
126
|
+
update_query = {}
|
127
|
+
for field, value in merged_update_query.items():
|
128
|
+
if field.startswith('$'):
|
129
|
+
update_query[field] = value
|
130
|
+
else:
|
131
|
+
update_query['$set'] = update_query.get('$set', {})
|
132
|
+
update_query['$set'][field] = value
|
118
133
|
|
119
|
-
result = await db.session[cls.__name__].update_one(_filter,
|
134
|
+
result = await db.session[cls.__name__].update_one(_filter, update_query)
|
120
135
|
return bool(result.matched_count)
|
121
136
|
|
122
137
|
@classmethod
|
panther/db/queries/queries.py
CHANGED
@@ -364,8 +364,6 @@ class Query(BaseQuery):
|
|
364
364
|
|
365
365
|
raise NotFoundAPIError(detail=f'{cls.__name__} Does Not Exist')
|
366
366
|
|
367
|
-
@check_connection
|
368
|
-
@log_query
|
369
367
|
async def save(self) -> None:
|
370
368
|
"""
|
371
369
|
Save the document
|
@@ -384,8 +382,14 @@ class Query(BaseQuery):
|
|
384
382
|
>>> user = User(name='Ali')
|
385
383
|
>>> await user.save()
|
386
384
|
"""
|
387
|
-
document = self.
|
385
|
+
document = {field: getattr(self, field) for field in self.model_fields_set if field != 'request'}
|
386
|
+
|
388
387
|
if self.id:
|
389
388
|
await self.update(document)
|
390
389
|
else:
|
391
390
|
await self.insert_one(document)
|
391
|
+
|
392
|
+
async def reload(self) -> Self:
|
393
|
+
new_obj = await self.find_one(id=self.id)
|
394
|
+
[setattr(self, f, getattr(new_obj, f)) for f in new_obj.model_fields]
|
395
|
+
return self
|
panther/file_handler.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from functools import cached_property
|
2
2
|
|
3
3
|
from panther import status
|
4
|
-
from pydantic import BaseModel, field_validator
|
4
|
+
from pydantic import BaseModel, field_validator, model_serializer
|
5
5
|
|
6
6
|
from panther.exceptions import APIError
|
7
7
|
|
@@ -15,6 +15,23 @@ class File(BaseModel):
|
|
15
15
|
def size(self):
|
16
16
|
return len(self.file)
|
17
17
|
|
18
|
+
def save(self) -> str:
|
19
|
+
if hasattr(self, '_file_name'):
|
20
|
+
return self._file_name
|
21
|
+
|
22
|
+
self._file_name = self.file_name
|
23
|
+
# TODO: check for duplication
|
24
|
+
with open(self._file_name, 'wb') as file:
|
25
|
+
file.write(self.file)
|
26
|
+
|
27
|
+
return self.file_name
|
28
|
+
|
29
|
+
@model_serializer(mode='wrap')
|
30
|
+
def _serialize(self, handler):
|
31
|
+
result = handler(self)
|
32
|
+
result['path'] = self.save()
|
33
|
+
return result
|
34
|
+
|
18
35
|
def __repr__(self) -> str:
|
19
36
|
return f'{self.__repr_name__()}(file_name={self.file_name}, content_type={self.content_type})'
|
20
37
|
|
panther/generics.py
CHANGED
@@ -23,7 +23,7 @@ logger = logging.getLogger('panther')
|
|
23
23
|
|
24
24
|
class ObjectRequired:
|
25
25
|
def _check_object(self, instance):
|
26
|
-
if issubclass(type(instance), Model) is False:
|
26
|
+
if instance and issubclass(type(instance), Model) is False:
|
27
27
|
logger.critical(f'`{self.__class__.__name__}.object()` should return instance of a Model --> `find_one()`')
|
28
28
|
raise APIError
|
29
29
|
|
@@ -129,9 +129,11 @@ class CreateAPI(GenericAPI):
|
|
129
129
|
input_model: type[ModelSerializer]
|
130
130
|
|
131
131
|
async def post(self, request: Request, **kwargs):
|
132
|
-
instance = await request.validated_data.create(
|
133
|
-
|
134
|
-
|
132
|
+
instance = await request.validated_data.create(validated_data={
|
133
|
+
field: getattr(request.validated_data, field)
|
134
|
+
for field in request.validated_data.model_fields_set
|
135
|
+
if field != 'request'
|
136
|
+
})
|
135
137
|
return Response(data=instance, status_code=status.HTTP_201_CREATED)
|
136
138
|
|
137
139
|
|
@@ -160,13 +162,30 @@ class UpdateAPI(GenericAPI, ObjectRequired):
|
|
160
162
|
|
161
163
|
|
162
164
|
class DeleteAPI(GenericAPI, ObjectRequired):
|
165
|
+
async def pre_delete(self, instance, request: Request, **kwargs):
|
166
|
+
pass
|
167
|
+
|
168
|
+
async def post_delete(self, instance, request: Request, **kwargs):
|
169
|
+
pass
|
170
|
+
|
163
171
|
async def delete(self, request: Request, **kwargs):
|
164
172
|
instance = await self.object(request=request, **kwargs)
|
165
173
|
self._check_object(instance)
|
166
174
|
|
175
|
+
await self.pre_delete(instance, request=request, **kwargs)
|
167
176
|
await instance.delete()
|
177
|
+
await self.post_delete(instance, request=request, **kwargs)
|
178
|
+
|
168
179
|
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
169
180
|
|
170
181
|
|
171
182
|
class ListCreateAPI(CreateAPI, ListAPI):
|
172
183
|
pass
|
184
|
+
|
185
|
+
|
186
|
+
class UpdateDeleteAPI(UpdateAPI, DeleteAPI):
|
187
|
+
pass
|
188
|
+
|
189
|
+
|
190
|
+
class RetrieveUpdateDeleteAPI(RetrieveAPI, UpdateAPI, DeleteAPI):
|
191
|
+
pass
|
panther/serializer.py
CHANGED
@@ -65,8 +65,8 @@ class MetaModelSerializer:
|
|
65
65
|
|
66
66
|
# Check `model` type
|
67
67
|
try:
|
68
|
-
if not issubclass(model, Model):
|
69
|
-
msg = f'`{cls_name}.Config.model` is not subclass of `panther.db.Model`.'
|
68
|
+
if not issubclass(model, (Model, BaseModel)):
|
69
|
+
msg = f'`{cls_name}.Config.model` is not subclass of `panther.db.Model` or `pydantic.BaseModel`.'
|
70
70
|
raise AttributeError(msg) from None
|
71
71
|
except TypeError:
|
72
72
|
msg = f'`{cls_name}.Config.model` is not subclass of `panther.db.Model`.'
|
@@ -1,7 +1,7 @@
|
|
1
|
-
panther/__init__.py,sha256=
|
1
|
+
panther/__init__.py,sha256=QrE4JPnhuhmVTr9unHaojWSt5hVyBwmbJq26yQOSFx8,110
|
2
2
|
panther/_load_configs.py,sha256=AVkoixkUFkBQiTmrLrwCmg0eiPW2U_Uw2EGNEGQRfnI,9281
|
3
3
|
panther/_utils.py,sha256=j0rwIxTf0rtcZAAD-1nGE-_bWpvinyKtnwt3uO0hMmY,4330
|
4
|
-
panther/app.py,sha256=
|
4
|
+
panther/app.py,sha256=uoJFc058fiuIu6JWIkpPOpYQ4e9Gcu1iZl7VYxaZf7M,7752
|
5
5
|
panther/authentications.py,sha256=gf7BVyQ8vXKhiumJAtD0aAK7uIHWx_snbOKYAKrYuVw,5677
|
6
6
|
panther/background_tasks.py,sha256=HBYubDIiO_673cl_5fqCUP9zzimzRgRkDSkag9Msnbs,7656
|
7
7
|
panther/base_request.py,sha256=Fwwpm-9bjAZdpzSdakmSas5BD3gh1nrc6iGcBxwa_94,4001
|
@@ -10,8 +10,8 @@ panther/caching.py,sha256=ltuJYdjNiAaKIs3jpO5EBpL8Y6CF1vAIQqh8J_Np10g,4098
|
|
10
10
|
panther/configs.py,sha256=EaLApT6nYcguBoNXBG_8n6DU6HTNxsulI2943j8UAkE,3174
|
11
11
|
panther/events.py,sha256=bxDqrfiNNBlvD03vEk2LDK4xbMzTMFVcgAjx2ein7mI,1158
|
12
12
|
panther/exceptions.py,sha256=7rHdJIES2__kqOStIqbHl3Uxask2lzKgLQlkZvvDwFA,1591
|
13
|
-
panther/file_handler.py,sha256=
|
14
|
-
panther/generics.py,sha256=
|
13
|
+
panther/file_handler.py,sha256=I94tpbtTVniBnnUMkFr3Eis6kPDt8sLzS5u8TzFrR5I,1323
|
14
|
+
panther/generics.py,sha256=D2ia7M4ML15kMZiuCIMpL7ZfQhMmKpqE4wCmuRE-q4Y,7233
|
15
15
|
panther/logging.py,sha256=k__vzvSrPpr1IsA4OLrBt1JHuRUBXr7ekPlBW0-9rbM,2209
|
16
16
|
panther/main.py,sha256=UbIxwaojvY_vH9nYfBpkulRBqVEj4Lbl81Er4XW_KCY,9334
|
17
17
|
panther/monitoring.py,sha256=y1F3c8FJlnmooM-m1nSyOTa9eWq0v1nHnmw9zz-4Kls,1314
|
@@ -20,7 +20,7 @@ panther/permissions.py,sha256=9-J5vzvEKa_PITwEVQbZZv8PG2FOu05YBlD5yMrKcfc,348
|
|
20
20
|
panther/request.py,sha256=F9ZiAWSse7_6moAzqdoFInUN4zTKlzijh9AdU9w3Jfw,1673
|
21
21
|
panther/response.py,sha256=Njp4zJozNic8J4ucG8Sgh-xeBZOgtoz2cfdDkJlGOWU,7582
|
22
22
|
panther/routings.py,sha256=1eqbjubLnUUEQRlz8mIF464ImvCMjyasiekHBtxEQoQ,6218
|
23
|
-
panther/serializer.py,sha256=
|
23
|
+
panther/serializer.py,sha256=UX-cVS-11KnxijUhPXsBs_Pb-Sm3EVzUQFTf9bFQT0A,9096
|
24
24
|
panther/status.py,sha256=Gc_PnYrHfInTsZpGbqiCfDB-py1C7Rh8KMdb6Lq9Exs,3346
|
25
25
|
panther/test.py,sha256=RsQtP5IURLWR__BihOjruWoX3NscmGDqDqj1CfAb3bI,7037
|
26
26
|
panther/throttling.py,sha256=mVa_mGv6w_Ad7LLtV4eG5QpDwwNsk4QjFFi0mIHQBnE,231
|
@@ -36,22 +36,22 @@ panther/cli/utils.py,sha256=Jd4YQ9H6lapVktl7ZmiORt30WVmKI85xcwsY-fMRq3c,5289
|
|
36
36
|
panther/db/__init__.py,sha256=w9lEL0vRqb18Qx_iUJipUR_fi5GQ5uVX0DWycx14x08,50
|
37
37
|
panther/db/connections.py,sha256=rps48Ic2r3SV2HD3df1OU7C4Pv8j98PVXU1O_FqF9Ak,4210
|
38
38
|
panther/db/cursor.py,sha256=jJ6bhz_Zljt3-AoeVdi563e2q3MSDJPP33WVbQk-goE,1287
|
39
|
-
panther/db/models.py,sha256=
|
39
|
+
panther/db/models.py,sha256=GRbKXJiwnxQJ_SjuPfXPzA5miH_djZNTPPyqiRp5DI8,2561
|
40
40
|
panther/db/utils.py,sha256=Uxh7UebkBv4thMCfooYW1pkuorFgocsbnBZJi-hHtdY,1582
|
41
41
|
panther/db/queries/__init__.py,sha256=uF4gvBjLBJ-Yl3WLqoZEVNtHCVhFRKW3_Vi44pJxDNI,45
|
42
42
|
panther/db/queries/base_queries.py,sha256=8HhdlsSW-lgz3-IrZYfOtHNC3TBWbCNErDR4XE718AY,3764
|
43
|
-
panther/db/queries/mongodb_queries.py,sha256=
|
43
|
+
panther/db/queries/mongodb_queries.py,sha256=AUteqnQdqjM3axYahOLDuxrYxgI-hQa72aXcoJx9ItU,5585
|
44
44
|
panther/db/queries/pantherdb_queries.py,sha256=_dA4gXk1IA5jzIy6_6o1zgdZeeka6SPihvQeSkj7h68,4481
|
45
|
-
panther/db/queries/queries.py,sha256=
|
45
|
+
panther/db/queries/queries.py,sha256=d6V4whHEYjtufoeqIC5lBNa_FGDW8_7AYeps48ilHDk,11665
|
46
46
|
panther/middlewares/__init__.py,sha256=ydo0bSadGqa2v7Xy1oCTkF2uXrImedXjiyx2vPTwPhE,66
|
47
47
|
panther/middlewares/base.py,sha256=tX0MBvDBkbsAB_DilRIYvcggSAqCzazRTb9MegZNdlA,843
|
48
48
|
panther/panel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
49
49
|
panther/panel/apis.py,sha256=COsbwKZyTgyHvHYbpDfusifAH9ojMS3z1KhZCt9M-Ms,2428
|
50
50
|
panther/panel/urls.py,sha256=JiV-H4dWE-m_bfaTTVxzOxTvJmOWhyLOvcbM7xU3Bn4,240
|
51
51
|
panther/panel/utils.py,sha256=0Rv79oR5IEqalqwpRKQHMn1p5duVY5mxMqDKiA5mWx4,437
|
52
|
-
panther-4.1.
|
53
|
-
panther-4.1.
|
54
|
-
panther-4.1.
|
55
|
-
panther-4.1.
|
56
|
-
panther-4.1.
|
57
|
-
panther-4.1.
|
52
|
+
panther-4.1.3.dist-info/LICENSE,sha256=2aF1hL2aC0zRPjzUkSxJUzZbn2_uLoOkn7DHjzZni-I,1524
|
53
|
+
panther-4.1.3.dist-info/METADATA,sha256=Fbqyl-q10f5RgrsihqeoywGlRIITbjX3C8vtZi7mQpA,6376
|
54
|
+
panther-4.1.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
55
|
+
panther-4.1.3.dist-info/entry_points.txt,sha256=6GPxYFGuzVfNB4YpHFJvYex6iWah5_tLnirAHwj2Qsg,51
|
56
|
+
panther-4.1.3.dist-info/top_level.txt,sha256=VbBs02JGXTIoHMzsX-eLOk2MCbBZzQbLhWiYpI7xI2g,8
|
57
|
+
panther-4.1.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|