GeneralManager 0.12.1__py3-none-any.whl → 0.13.0__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.
- general_manager/api/graphql.py +8 -2
- general_manager/bucket/baseBucket.py +18 -9
- general_manager/bucket/calculationBucket.py +31 -17
- general_manager/bucket/databaseBucket.py +14 -4
- general_manager/bucket/groupBucket.py +12 -2
- general_manager/interface/databaseBasedInterface.py +46 -9
- general_manager/interface/databaseInterface.py +16 -9
- general_manager/interface/models.py +1 -1
- general_manager/permission/managerBasedPermission.py +59 -10
- general_manager/permission/permissionChecks.py +5 -0
- {generalmanager-0.12.1.dist-info → generalmanager-0.13.0.dist-info}/METADATA +1 -1
- {generalmanager-0.12.1.dist-info → generalmanager-0.13.0.dist-info}/RECORD +15 -15
- {generalmanager-0.12.1.dist-info → generalmanager-0.13.0.dist-info}/WHEEL +0 -0
- {generalmanager-0.12.1.dist-info → generalmanager-0.13.0.dist-info}/licenses/LICENSE +0 -0
- {generalmanager-0.12.1.dist-info → generalmanager-0.13.0.dist-info}/top_level.txt +0 -0
general_manager/api/graphql.py
CHANGED
@@ -384,10 +384,16 @@ class GraphQL:
|
|
384
384
|
info: GraphQLResolveInfo,
|
385
385
|
) -> Bucket:
|
386
386
|
"""
|
387
|
-
|
387
|
+
Applies permission-based filters to a queryset according to the permission interface of the given manager class.
|
388
|
+
|
389
|
+
Returns:
|
390
|
+
A queryset containing only the items allowed by the user's read permissions. If no permission filters are defined, returns the original queryset unchanged.
|
388
391
|
"""
|
389
392
|
permission_filters = getReadPermissionFilter(general_manager_class, info)
|
390
|
-
|
393
|
+
if not permission_filters:
|
394
|
+
return queryset
|
395
|
+
|
396
|
+
filtered_queryset = queryset.none()
|
391
397
|
for perm_filter, perm_exclude in permission_filters:
|
392
398
|
qs_perm = queryset.exclude(**perm_exclude).filter(**perm_filter)
|
393
399
|
filtered_queryset = filtered_queryset | qs_perm
|
@@ -61,13 +61,13 @@ class Bucket(ABC, Generic[GeneralManagerType]):
|
|
61
61
|
other: Bucket[GeneralManagerType] | GeneralManagerType,
|
62
62
|
) -> Bucket[GeneralManagerType]:
|
63
63
|
"""
|
64
|
-
Return a new bucket
|
64
|
+
Return a new bucket representing the union of this bucket and another bucket or a single manager instance.
|
65
65
|
|
66
66
|
Parameters:
|
67
|
-
other: Another bucket or a single manager instance to
|
67
|
+
other: Another bucket or a single manager instance to include in the union.
|
68
68
|
|
69
69
|
Returns:
|
70
|
-
A new bucket containing all unique items from both
|
70
|
+
A new bucket containing all unique items from both this bucket and the provided argument.
|
71
71
|
"""
|
72
72
|
raise NotImplementedError
|
73
73
|
|
@@ -229,14 +229,23 @@ class Bucket(ABC, Generic[GeneralManagerType]):
|
|
229
229
|
|
230
230
|
def group_by(self, *group_by_keys: str) -> GroupBucket[GeneralManagerType]:
|
231
231
|
"""
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
*group_by_keys:
|
236
|
-
|
232
|
+
Return a GroupBucket that groups the items in this bucket by the specified attribute keys.
|
233
|
+
|
234
|
+
Parameters:
|
235
|
+
*group_by_keys (str): Attribute names to group the items by.
|
236
|
+
|
237
237
|
Returns:
|
238
|
-
A
|
238
|
+
GroupBucket[GeneralManagerType]: A bucket containing items grouped by the given keys.
|
239
239
|
"""
|
240
240
|
from general_manager.bucket.groupBucket import GroupBucket
|
241
241
|
|
242
242
|
return GroupBucket(self._manager_class, group_by_keys, self)
|
243
|
+
|
244
|
+
def none(self) -> Bucket[GeneralManagerType]:
|
245
|
+
"""
|
246
|
+
Raise NotImplementedError to indicate that subclasses must implement a method returning an empty bucket.
|
247
|
+
"""
|
248
|
+
raise NotImplementedError(
|
249
|
+
"The 'none' method is not implemented in the base Bucket class. "
|
250
|
+
"Subclasses should implement this method to return an empty bucket."
|
251
|
+
)
|
@@ -9,6 +9,7 @@ from typing import (
|
|
9
9
|
Generator,
|
10
10
|
List,
|
11
11
|
)
|
12
|
+
from copy import deepcopy
|
12
13
|
from general_manager.interface.baseInterface import (
|
13
14
|
generalManagerClassName,
|
14
15
|
GeneralManagerType,
|
@@ -95,13 +96,13 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
95
96
|
other: Bucket[GeneralManagerType] | GeneralManagerType,
|
96
97
|
) -> CalculationBucket[GeneralManagerType]:
|
97
98
|
"""
|
98
|
-
Combine this CalculationBucket with another bucket or manager instance of the same manager class.
|
99
|
+
Combine this CalculationBucket with another bucket or a manager instance of the same manager class.
|
99
100
|
|
100
|
-
If combined with a manager instance, returns a bucket filtered to that
|
101
|
+
If combined with a manager instance, returns a bucket filtered to that instance's identification. If combined with another CalculationBucket of the same manager class, returns a new bucket containing only the filters and excludes that are present and identical in both buckets.
|
101
102
|
|
102
103
|
Raises:
|
103
104
|
ValueError: If the other object is not a CalculationBucket or manager of the same class.
|
104
|
-
|
105
|
+
|
105
106
|
Returns:
|
106
107
|
CalculationBucket[GeneralManagerType]: A new CalculationBucket representing the intersection of filters and excludes, or a filtered bucket for the given manager instance.
|
107
108
|
"""
|
@@ -183,17 +184,18 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
183
184
|
|
184
185
|
def all(self) -> CalculationBucket:
|
185
186
|
"""
|
186
|
-
|
187
|
-
|
188
|
-
|
187
|
+
Return a deep copy of the current CalculationBucket instance.
|
188
|
+
|
189
|
+
Use this method to obtain an independent copy of the bucket, ensuring that modifications to the returned instance do not affect the original.
|
189
190
|
"""
|
190
|
-
return self
|
191
|
+
return deepcopy(self)
|
191
192
|
|
192
193
|
def __iter__(self) -> Generator[GeneralManagerType, None, None]:
|
193
194
|
"""
|
194
|
-
|
195
|
-
|
196
|
-
|
195
|
+
Iterate over all valid input combinations, yielding a manager instance for each.
|
196
|
+
|
197
|
+
Yields:
|
198
|
+
Manager instances created with each valid set of input parameters.
|
197
199
|
"""
|
198
200
|
combinations = self.generate_combinations()
|
199
201
|
for combo in combinations:
|
@@ -470,15 +472,27 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
470
472
|
self, key: str | tuple[str], reverse: bool = False
|
471
473
|
) -> CalculationBucket[GeneralManagerType]:
|
472
474
|
"""
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
key
|
477
|
-
reverse:
|
478
|
-
|
475
|
+
Return a new CalculationBucket instance with updated sorting criteria.
|
476
|
+
|
477
|
+
Parameters:
|
478
|
+
key (str or tuple of str): Field name(s) to sort the combinations by.
|
479
|
+
reverse (bool): Whether to sort in descending order.
|
480
|
+
|
479
481
|
Returns:
|
480
|
-
A new
|
482
|
+
CalculationBucket: A new bucket instance sorted according to the specified key and order.
|
481
483
|
"""
|
482
484
|
return CalculationBucket(
|
483
485
|
self._manager_class, self.filters, self.excludes, key, reverse
|
484
486
|
)
|
487
|
+
|
488
|
+
def none(self) -> CalculationBucket[GeneralManagerType]:
|
489
|
+
"""
|
490
|
+
Return a new CalculationBucket instance of the same type containing no items.
|
491
|
+
|
492
|
+
The returned bucket has all filters, excludes, and cached combinations cleared, representing an empty set of combinations.
|
493
|
+
"""
|
494
|
+
own = self.all()
|
495
|
+
own._current_combinations = None
|
496
|
+
own.filters = {}
|
497
|
+
own.excludes = {}
|
498
|
+
return own
|
@@ -211,14 +211,14 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
211
211
|
reverse: bool = False,
|
212
212
|
) -> DatabaseBucket:
|
213
213
|
"""
|
214
|
-
Return a new DatabaseBucket sorted by the specified field or fields.
|
215
|
-
|
214
|
+
Return a new DatabaseBucket with items sorted by the specified field or fields.
|
215
|
+
|
216
216
|
Parameters:
|
217
217
|
key (str or tuple of str): Field name or tuple of field names to sort by.
|
218
218
|
reverse (bool): If True, sort in descending order.
|
219
|
-
|
219
|
+
|
220
220
|
Returns:
|
221
|
-
DatabaseBucket: A new bucket
|
221
|
+
DatabaseBucket: A new bucket containing the sorted items.
|
222
222
|
"""
|
223
223
|
if isinstance(key, str):
|
224
224
|
key = (key,)
|
@@ -227,3 +227,13 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
227
227
|
else:
|
228
228
|
sorted_data = self._data.order_by(*key)
|
229
229
|
return self.__class__(sorted_data, self._manager_class)
|
230
|
+
|
231
|
+
def none(self) -> DatabaseBucket[GeneralManagerType]:
|
232
|
+
"""
|
233
|
+
Return a new DatabaseBucket instance of the same type containing no items.
|
234
|
+
|
235
|
+
This method creates an empty bucket while preserving the current manager class and bucket type.
|
236
|
+
"""
|
237
|
+
own = self.all()
|
238
|
+
own._data = own._data.none()
|
239
|
+
return own
|
@@ -285,8 +285,8 @@ class GroupBucket(Bucket[GeneralManagerType]):
|
|
285
285
|
|
286
286
|
def group_by(self, *group_by_keys: str) -> GroupBucket[GeneralManagerType]:
|
287
287
|
"""
|
288
|
-
|
289
|
-
|
288
|
+
Return a new GroupBucket grouped by the current and additional attribute keys.
|
289
|
+
|
290
290
|
Additional group-by keys are appended to the existing grouping, and the new GroupBucket is constructed from the same underlying data.
|
291
291
|
"""
|
292
292
|
return GroupBucket(
|
@@ -294,3 +294,13 @@ class GroupBucket(Bucket[GeneralManagerType]):
|
|
294
294
|
tuple([*self._group_by_keys, *group_by_keys]),
|
295
295
|
self._basis_data,
|
296
296
|
)
|
297
|
+
|
298
|
+
def none(self) -> GroupBucket[GeneralManagerType]:
|
299
|
+
"""
|
300
|
+
Return a new empty GroupBucket with the same manager class and group-by keys as the current instance.
|
301
|
+
|
302
|
+
This method creates a GroupBucket containing no items, preserving the grouping configuration of the original.
|
303
|
+
"""
|
304
|
+
return GroupBucket(
|
305
|
+
self._manager_class, self._group_by_keys, self._basis_data.none()
|
306
|
+
)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
from typing import Type, Any, Callable, TYPE_CHECKING, TypeVar, Generic, cast
|
3
3
|
from django.db import models
|
4
|
+
|
4
5
|
from datetime import datetime, timedelta
|
5
6
|
from general_manager.measurement.measurement import Measurement
|
6
7
|
from general_manager.measurement.measurementField import MeasurementField
|
@@ -66,6 +67,25 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
66
67
|
instance = self.getHistoricalRecord(instance, search_date)
|
67
68
|
return instance
|
68
69
|
|
70
|
+
@staticmethod
|
71
|
+
def __parseKwargs(**kwargs: Any) -> dict[str, Any]:
|
72
|
+
"""
|
73
|
+
Parses keyword arguments to ensure they are compatible with the model's fields.
|
74
|
+
|
75
|
+
Converts GeneralManager instances to their primary key values and returns a dictionary of parsed arguments.
|
76
|
+
"""
|
77
|
+
from general_manager.manager.generalManager import GeneralManager
|
78
|
+
|
79
|
+
parsed_kwargs: dict[str, Any] = {}
|
80
|
+
for key, value in kwargs.items():
|
81
|
+
if isinstance(value, GeneralManager):
|
82
|
+
parsed_kwargs[key] = getattr(
|
83
|
+
value._interface, "_instance", value.identification["id"]
|
84
|
+
)
|
85
|
+
else:
|
86
|
+
parsed_kwargs[key] = value
|
87
|
+
return parsed_kwargs
|
88
|
+
|
69
89
|
@classmethod
|
70
90
|
def filter(cls, **kwargs: Any) -> DatabaseBucket:
|
71
91
|
"""
|
@@ -77,6 +97,9 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
77
97
|
Returns:
|
78
98
|
A DatabaseBucket wrapping the filtered queryset and associated metadata.
|
79
99
|
"""
|
100
|
+
|
101
|
+
kwargs = cls.__parseKwargs(**kwargs)
|
102
|
+
|
80
103
|
return DatabaseBucket(
|
81
104
|
cls._model.objects.filter(**kwargs),
|
82
105
|
cls._parent_class,
|
@@ -94,6 +117,8 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
94
117
|
Returns:
|
95
118
|
A DatabaseBucket wrapping the queryset of excluded model instances.
|
96
119
|
"""
|
120
|
+
kwargs = cls.__parseKwargs(**kwargs)
|
121
|
+
|
97
122
|
return DatabaseBucket(
|
98
123
|
cls._model.objects.exclude(**kwargs),
|
99
124
|
cls._parent_class,
|
@@ -135,9 +160,9 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
135
160
|
@classmethod
|
136
161
|
def getAttributeTypes(cls) -> dict[str, AttributeTypedDict]:
|
137
162
|
"""
|
138
|
-
Return a dictionary mapping each attribute name
|
163
|
+
Return a dictionary mapping each model attribute name to its type information and metadata.
|
139
164
|
|
140
|
-
|
165
|
+
Includes standard fields, custom fields, foreign keys, many-to-many, and reverse relation fields, excluding GenericForeignKey fields. For each attribute, provides its Python type (translated from Django field types when possible), required and editable status, whether it is derived, and its default value. For related models with a general manager class, the type is set to that class.
|
141
166
|
|
142
167
|
Returns:
|
143
168
|
dict[str, AttributeTypedDict]: Mapping of attribute names to their type information and metadata.
|
@@ -178,7 +203,8 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
178
203
|
fields[field_name] = {
|
179
204
|
"type": type(field),
|
180
205
|
"is_derived": False,
|
181
|
-
"is_required": not field.null
|
206
|
+
"is_required": not field.null
|
207
|
+
and field.default is models.NOT_PROVIDED,
|
182
208
|
"is_editable": field.editable,
|
183
209
|
"default": field.default,
|
184
210
|
}
|
@@ -196,7 +222,7 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
196
222
|
):
|
197
223
|
related_model = related_model._general_manager_class # type: ignore
|
198
224
|
|
199
|
-
|
225
|
+
if related_model is not None:
|
200
226
|
default = None
|
201
227
|
if hasattr(field, "default"):
|
202
228
|
default = field.default # type: ignore
|
@@ -247,12 +273,12 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
247
273
|
@classmethod
|
248
274
|
def getAttributes(cls) -> dict[str, Callable[[DBBasedInterface], Any]]:
|
249
275
|
"""
|
250
|
-
Return a
|
276
|
+
Return a mapping of attribute names to callables that extract values from a DBBasedInterface instance.
|
251
277
|
|
252
|
-
The
|
278
|
+
The returned dictionary includes accessors for custom fields, standard model fields, foreign keys, many-to-many relations, and reverse relations. For related models with a general manager class, the accessor returns an instance or queryset of that class; otherwise, it returns the related object or queryset directly. Raises a ValueError if a field name conflict is detected.
|
253
279
|
|
254
280
|
Returns:
|
255
|
-
dict
|
281
|
+
dict[str, Callable[[DBBasedInterface], Any]]: Mapping of attribute names to callables for retrieving values from a DBBasedInterface instance.
|
256
282
|
"""
|
257
283
|
from general_manager.manager.generalManager import GeneralManager
|
258
284
|
|
@@ -306,12 +332,22 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
306
332
|
cls._model._meta.get_field(field_name).related_model,
|
307
333
|
"_general_manager_class",
|
308
334
|
):
|
335
|
+
related_model = cast(
|
336
|
+
Type[models.Model],
|
337
|
+
cls._model._meta.get_field(field_name).related_model,
|
338
|
+
)
|
339
|
+
related_fields = [
|
340
|
+
f
|
341
|
+
for f in related_model._meta.get_fields()
|
342
|
+
if f.related_model == cls._model
|
343
|
+
]
|
344
|
+
|
309
345
|
field_values[
|
310
346
|
f"{field_name}_list"
|
311
|
-
] = lambda self, field_name=field_name: self._instance._meta.get_field(
|
347
|
+
] = lambda self, field_name=field_name, related_fields=related_fields: self._instance._meta.get_field(
|
312
348
|
field_name
|
313
349
|
).related_model._general_manager_class.filter(
|
314
|
-
**{
|
350
|
+
**{related_field.name: self.pk for related_field in related_fields}
|
315
351
|
)
|
316
352
|
else:
|
317
353
|
field_values[f"{field_name}_list"] = (
|
@@ -319,6 +355,7 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
|
|
319
355
|
self._instance, field_call
|
320
356
|
).all()
|
321
357
|
)
|
358
|
+
|
322
359
|
return field_values
|
323
360
|
|
324
361
|
@staticmethod
|
@@ -84,19 +84,26 @@ class DatabaseInterface(DBBasedInterface[GeneralManagerModel]):
|
|
84
84
|
instance: GeneralManagerModel, many_to_many_kwargs: dict[str, list[Any]]
|
85
85
|
) -> GeneralManagerModel:
|
86
86
|
"""
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
many_to_many_kwargs: A dictionary containing many-to-many field names and their corresponding values.
|
92
|
-
|
87
|
+
Set many-to-many relationship fields on a model instance using the provided values.
|
88
|
+
|
89
|
+
For each field, converts lists of `GeneralManager` instances to their IDs if necessary, and updates the corresponding many-to-many relationship on the instance. Fields with values of `None` or `NOT_PROVIDED` are ignored.
|
90
|
+
|
93
91
|
Returns:
|
94
|
-
The updated model instance.
|
92
|
+
GeneralManagerModel: The updated model instance with many-to-many relationships set.
|
95
93
|
"""
|
94
|
+
from general_manager.manager.generalManager import GeneralManager
|
95
|
+
|
96
96
|
for key, value in many_to_many_kwargs.items():
|
97
|
-
if
|
97
|
+
if value is None or value is NOT_PROVIDED:
|
98
98
|
continue
|
99
|
-
field_name = key.
|
99
|
+
field_name = key.removesuffix("_id_list")
|
100
|
+
if isinstance(value, list) and all(
|
101
|
+
isinstance(v, GeneralManager) for v in value
|
102
|
+
):
|
103
|
+
value = [
|
104
|
+
v.identification["id"] if hasattr(v, "identification") else v
|
105
|
+
for v in value
|
106
|
+
]
|
100
107
|
getattr(instance, field_name).set(value)
|
101
108
|
|
102
109
|
return instance
|
@@ -39,7 +39,7 @@ def getFullCleanMethode(model: Type[models.Model]) -> Callable[..., None]:
|
|
39
39
|
except ValidationError as e:
|
40
40
|
errors.update(e.message_dict)
|
41
41
|
|
42
|
-
rules: list[Rule] = getattr(self._meta, "rules")
|
42
|
+
rules: list[Rule] = getattr(self._meta, "rules", [])
|
43
43
|
for rule in rules:
|
44
44
|
if rule.evaluate(self) is False:
|
45
45
|
error_message = rule.getErrorMessage()
|
@@ -17,20 +17,30 @@ type permission_type = Literal[
|
|
17
17
|
]
|
18
18
|
|
19
19
|
|
20
|
+
class notExistent:
|
21
|
+
pass
|
22
|
+
|
23
|
+
|
20
24
|
class ManagerBasedPermission(BasePermission):
|
21
25
|
__based_on__: Optional[str] = None
|
22
|
-
__read__: list[str]
|
23
|
-
__create__: list[str]
|
24
|
-
__update__: list[str]
|
25
|
-
__delete__: list[str]
|
26
|
+
__read__: list[str]
|
27
|
+
__create__: list[str]
|
28
|
+
__update__: list[str]
|
29
|
+
__delete__: list[str]
|
26
30
|
|
27
31
|
def __init__(
|
28
32
|
self,
|
29
33
|
instance: PermissionDataManager | GeneralManager,
|
30
34
|
request_user: AbstractUser,
|
31
35
|
) -> None:
|
32
|
-
|
36
|
+
"""
|
37
|
+
Initializes the ManagerBasedPermission with a manager instance and the requesting user.
|
38
|
+
|
39
|
+
Configures default CRUD permissions, collects attribute-specific permissions, and sets up any related "based on" permission for cascading checks.
|
40
|
+
"""
|
33
41
|
super().__init__(instance, request_user)
|
42
|
+
self.__setPermissions()
|
43
|
+
|
34
44
|
self.__attribute_permissions = self.__getAttributePermissions()
|
35
45
|
self.__based_on_permission = self.__getBasedOnPermission()
|
36
46
|
self.__overall_results: Dict[permission_type, Optional[bool]] = {
|
@@ -40,20 +50,52 @@ class ManagerBasedPermission(BasePermission):
|
|
40
50
|
"delete": None,
|
41
51
|
}
|
42
52
|
|
53
|
+
def __setPermissions(self, skip_based_on: bool = False) -> None:
|
54
|
+
|
55
|
+
"""
|
56
|
+
Assigns default permission lists for CRUD actions based on the presence of a related permission attribute.
|
57
|
+
|
58
|
+
If the permission is based on another attribute and `skip_based_on` is False, all default permissions are set to empty lists. Otherwise, read permissions default to `["public"]` and write permissions to `["isAuthenticated"]`. Class-level overrides are respected if present.
|
59
|
+
"""
|
60
|
+
default_read = ["public"]
|
61
|
+
default_write = ["isAuthenticated"]
|
62
|
+
|
63
|
+
if self.__based_on__ is not None and not skip_based_on:
|
64
|
+
default_read = []
|
65
|
+
default_write = []
|
66
|
+
|
67
|
+
self.__read__ = getattr(self.__class__, "__read__", default_read)
|
68
|
+
self.__create__ = getattr(self.__class__, "__create__", default_write)
|
69
|
+
self.__update__ = getattr(self.__class__, "__update__", default_write)
|
70
|
+
self.__delete__ = getattr(self.__class__, "__delete__", default_write)
|
71
|
+
|
43
72
|
def __getBasedOnPermission(self) -> Optional[BasePermission]:
|
73
|
+
"""
|
74
|
+
Retrieves the permission object associated with the `__based_on__` attribute, if present and valid.
|
75
|
+
|
76
|
+
Returns:
|
77
|
+
An instance of the related `BasePermission` subclass if the `__based_on__` attribute exists on the instance and its `Permission` class is a subclass of `BasePermission`; otherwise, returns `None`.
|
78
|
+
|
79
|
+
Raises:
|
80
|
+
ValueError: If the `__based_on__` attribute is missing from the instance.
|
81
|
+
TypeError: If the `__based_on__` attribute is not a `GeneralManager` or its subclass.
|
82
|
+
"""
|
44
83
|
from general_manager.manager.generalManager import GeneralManager
|
45
84
|
|
46
85
|
__based_on__ = getattr(self, "__based_on__")
|
47
86
|
if __based_on__ is None:
|
48
87
|
return None
|
49
88
|
|
50
|
-
basis_object = getattr(self.instance, __based_on__,
|
51
|
-
if basis_object is
|
89
|
+
basis_object = getattr(self.instance, __based_on__, notExistent)
|
90
|
+
if basis_object is notExistent:
|
52
91
|
raise ValueError(
|
53
|
-
f"Based on
|
92
|
+
f"Based on configuration '{__based_on__}' is not valid or does not exist."
|
54
93
|
)
|
55
|
-
if
|
56
|
-
|
94
|
+
if basis_object is None:
|
95
|
+
self.__setPermissions(skip_based_on=True)
|
96
|
+
return None
|
97
|
+
if not isinstance(basis_object, GeneralManager) and not (
|
98
|
+
isinstance(basis_object, type) and issubclass(basis_object, GeneralManager)
|
57
99
|
):
|
58
100
|
raise TypeError(f"Based on object {__based_on__} is not a GeneralManager")
|
59
101
|
|
@@ -124,6 +166,13 @@ class ManagerBasedPermission(BasePermission):
|
|
124
166
|
self,
|
125
167
|
permissions: list[str],
|
126
168
|
) -> bool:
|
169
|
+
"""
|
170
|
+
Return True if no permissions are required or if at least one permission string is valid for the user.
|
171
|
+
|
172
|
+
If the permissions list is empty, access is granted. Otherwise, returns True if any permission string in the list is validated for the user; returns False if none are valid.
|
173
|
+
"""
|
174
|
+
if not permissions:
|
175
|
+
return True
|
127
176
|
for permission in permissions:
|
128
177
|
if self.validatePermissionString(permission):
|
129
178
|
return True
|
@@ -34,6 +34,11 @@ permission_functions: dict[str, PermissionDict] = {
|
|
34
34
|
"permission_method": lambda instance, user, config: True,
|
35
35
|
"permission_filter": lambda user, config: None,
|
36
36
|
},
|
37
|
+
"matches": {
|
38
|
+
"permission_method": lambda instance, user, config: getattr(instance, config[0])
|
39
|
+
== config[1],
|
40
|
+
"permission_filter": lambda user, config: {"filter": {config[0]: config[1]}},
|
41
|
+
},
|
37
42
|
"ends_with": {
|
38
43
|
"permission_method": lambda instance, user, config: getattr(
|
39
44
|
instance, config[0]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: GeneralManager
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.13.0
|
4
4
|
Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
|
5
5
|
Author-email: Tim Kleindick <tkleindick@yahoo.de>
|
6
6
|
License-Expression: MIT
|
@@ -1,12 +1,12 @@
|
|
1
1
|
general_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
2
|
general_manager/apps.py,sha256=ii-48klC2kBRjxtdQU9-RtPq66lZ8fDa7BmqyH-mbzk,9904
|
3
|
-
general_manager/api/graphql.py,sha256=
|
3
|
+
general_manager/api/graphql.py,sha256=x-ky5UTQc-ZM_RB__swtwc95QXmv-q-OqgiMzY8EUM0,39652
|
4
4
|
general_manager/api/mutation.py,sha256=9T892INzH4ip5sX3_BxB2WX145zt8S_0HGVPhGzyXZA,6736
|
5
5
|
general_manager/api/property.py,sha256=oc93p1P8dcIvrNorRuqD1EJVsd6eYttYhZuAS0s28gs,696
|
6
|
-
general_manager/bucket/baseBucket.py,sha256=
|
7
|
-
general_manager/bucket/calculationBucket.py,sha256=
|
8
|
-
general_manager/bucket/databaseBucket.py,sha256=
|
9
|
-
general_manager/bucket/groupBucket.py,sha256=
|
6
|
+
general_manager/bucket/baseBucket.py,sha256=RdEaUhbCVPGI9l9B3l9lC9Ma2pGjQx6NL1rlCEIPXEc,8243
|
7
|
+
general_manager/bucket/calculationBucket.py,sha256=AWXftX9X3N6lwRNwCnLfgxDDKOquwtSWpIan_OxYsOc,19359
|
8
|
+
general_manager/bucket/databaseBucket.py,sha256=sA7EAwEQxCYqlwI1l1Z6n4mgG2pEwkzeRdOBzdlzmbo,9554
|
9
|
+
general_manager/bucket/groupBucket.py,sha256=CoXQoHtXcNa_KLnvUOMG1j1TcCNeenlIMb-ORrK08LQ,11381
|
10
10
|
general_manager/cache/cacheDecorator.py,sha256=XQvs322lDDafecS6osPKmf7DyuZgDq8kuQaYpMeXXYg,3264
|
11
11
|
general_manager/cache/cacheTracker.py,sha256=rRw3OhBDf86hTC2Xbt1ocRgZqwu8_kXk4lczamcADFg,2955
|
12
12
|
general_manager/cache/dependencyIndex.py,sha256=lxD7IfnWVsBNt9l0_yDfJlHDRHAFC7N7p-Typ2tJp88,11044
|
@@ -19,9 +19,9 @@ general_manager/factory/factoryMethods.py,sha256=9Bag891j0XHe3dUBAFi7gUKcKeUwcBZ
|
|
19
19
|
general_manager/interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
20
|
general_manager/interface/baseInterface.py,sha256=cFsDU-nhj_O6Gir3eO0ukGKNn9Pplhe6gEMccHNi4O0,8648
|
21
21
|
general_manager/interface/calculationInterface.py,sha256=fTD3WQpsn3ImaxGW5S-JwVJyJJPoPp2mR6lAambdB8U,4755
|
22
|
-
general_manager/interface/databaseBasedInterface.py,sha256=
|
23
|
-
general_manager/interface/databaseInterface.py,sha256=
|
24
|
-
general_manager/interface/models.py,sha256=
|
22
|
+
general_manager/interface/databaseBasedInterface.py,sha256=uB4_kJ1kNSK-QnUrFKR0zrDP-UulBKeeHIslXqEdU4Q,23058
|
23
|
+
general_manager/interface/databaseInterface.py,sha256=NHXOJycY40pywzE1d_5Fi-pLVnmLR4OQwhAIHsgkM_g,7249
|
24
|
+
general_manager/interface/models.py,sha256=iYuSTMWKGrH5cjmxTii8HRpSmUUMhtw6xvatRzB4zuA,3315
|
25
25
|
general_manager/interface/readOnlyInterface.py,sha256=TkfbOeaa2wCq5kCv0a3IwJWcYOTVbtNsdNWmGAz0Mns,11217
|
26
26
|
general_manager/manager/__init__.py,sha256=l3RYp62aEhj3Y975_XUTIzo35LUnkTJHkb_hgChnXXI,111
|
27
27
|
general_manager/manager/generalManager.py,sha256=4Qn9TYpZpqh5qC95BEAQhpiZgDrRXrAJjO2BbbXUdNg,9129
|
@@ -34,9 +34,9 @@ general_manager/measurement/measurementField.py,sha256=hesh8YMQqBuX-thcMUcawHpQW
|
|
34
34
|
general_manager/permission/__init__.py,sha256=5UlDERN60Vn8obGVkT-cOM8kHjzmoxgK5w5FgTCDhGE,59
|
35
35
|
general_manager/permission/basePermission.py,sha256=DeiAX2sQQhtdquO13jys2MSkp0kPdg2oo7PSqB9q5Bw,5653
|
36
36
|
general_manager/permission/fileBasedPermission.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
|
-
general_manager/permission/managerBasedPermission.py,sha256=
|
37
|
+
general_manager/permission/managerBasedPermission.py,sha256=CANZu5sU-GhzoFdR87aJ0rX0_rC4a38nqV76IpzIPQQ,7893
|
38
38
|
general_manager/permission/mutationPermission.py,sha256=tYeunZQOJiUkQIqW0lU0KafwJKaTkrHTYKi7zV0wqHI,2972
|
39
|
-
general_manager/permission/permissionChecks.py,sha256=
|
39
|
+
general_manager/permission/permissionChecks.py,sha256=s4XBQtfWK_6_AByVRFbEE3_UD4rXU_9y58L5oeSPXLM,1876
|
40
40
|
general_manager/permission/permissionDataManager.py,sha256=EzF-XKJZKvKdNpF77ATYJ_VXSF09L-HTFe8h8p-JdB4,1784
|
41
41
|
general_manager/permission/utils.py,sha256=_cznQLk-wmK35pqs3dtgY6I94Ahyz_bTSmpkT0-MQnI,1277
|
42
42
|
general_manager/rule/__init__.py,sha256=4Har5cfPD1fmOsilTDod-ZUz3Com-tkl58jz7yY4fD0,23
|
@@ -51,8 +51,8 @@ general_manager/utils/makeCacheKey.py,sha256=UlFsxHXgsYy69AAelkF6GDvY4h7AImT2bBn
|
|
51
51
|
general_manager/utils/noneToZero.py,sha256=KfQtMQnrT6vsYST0K7lv6pVujkDcK3XL8czHYOhgqKQ,539
|
52
52
|
general_manager/utils/pathMapping.py,sha256=nrz5owQg2a69Yig1eCXorR9U0NSw7NmBAk5OkeoHTdA,6842
|
53
53
|
general_manager/utils/testing.py,sha256=ElZ8p4iZHxsHjDN8Lm5TmI6527CW747ltDOmtY6gAhk,11872
|
54
|
-
generalmanager-0.
|
55
|
-
generalmanager-0.
|
56
|
-
generalmanager-0.
|
57
|
-
generalmanager-0.
|
58
|
-
generalmanager-0.
|
54
|
+
generalmanager-0.13.0.dist-info/licenses/LICENSE,sha256=YGFm0ieb4KpkMRRt2qnWue6uFh0cUMtobwEBkHwajhc,1450
|
55
|
+
generalmanager-0.13.0.dist-info/METADATA,sha256=_0sQJIW1Rz4ZjoxXUgfKDHSDEqkiUszY5YIVR3cnO3c,6206
|
56
|
+
generalmanager-0.13.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
57
|
+
generalmanager-0.13.0.dist-info/top_level.txt,sha256=sTDtExP9ga-YP3h3h42yivUY-A2Q23C2nw6LNKOho4I,16
|
58
|
+
generalmanager-0.13.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|