GeneralManager 0.10.3__tar.gz → 0.10.4__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.
- {generalmanager-0.10.3 → generalmanager-0.10.4}/GeneralManager.egg-info/PKG-INFO +1 -1
- {generalmanager-0.10.3 → generalmanager-0.10.4}/PKG-INFO +1 -1
- {generalmanager-0.10.3 → generalmanager-0.10.4}/pyproject.toml +1 -1
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/api/graphql.py +31 -38
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/apps.py +6 -6
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/bucket/baseBucket.py +5 -8
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/bucket/calculationBucket.py +8 -9
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/bucket/databaseBucket.py +18 -31
- generalmanager-0.10.4/src/general_manager/cache/signals.py +60 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/factory/autoFactory.py +34 -34
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/factory/factories.py +73 -43
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/interface/baseInterface.py +3 -3
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/interface/databaseBasedInterface.py +63 -43
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/manager/generalManager.py +52 -42
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/manager/meta.py +19 -10
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/measurement/measurementField.py +19 -3
- generalmanager-0.10.3/src/general_manager/cache/signals.py +0 -48
- {generalmanager-0.10.3 → generalmanager-0.10.4}/GeneralManager.egg-info/SOURCES.txt +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/GeneralManager.egg-info/dependency_links.txt +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/GeneralManager.egg-info/requires.txt +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/GeneralManager.egg-info/top_level.txt +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/LICENSE +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/README.md +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/setup.cfg +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/__init__.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/api/mutation.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/api/property.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/bucket/groupBucket.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/cache/cacheDecorator.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/cache/cacheTracker.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/cache/dependencyIndex.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/cache/modelDependencyCollector.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/factory/__init__.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/factory/factoryMethods.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/interface/__init__.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/interface/calculationInterface.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/interface/databaseInterface.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/interface/models.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/interface/readOnlyInterface.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/manager/__init__.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/manager/groupManager.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/manager/input.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/measurement/__init__.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/measurement/measurement.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/permission/__init__.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/permission/basePermission.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/permission/fileBasedPermission.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/permission/managerBasedPermission.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/permission/permissionChecks.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/permission/permissionDataManager.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/rule/__init__.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/rule/handler.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/rule/rule.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/utils/__init__.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/utils/argsToKwargs.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/utils/filterParser.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/utils/formatString.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/utils/jsonEncoder.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/utils/makeCacheKey.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/utils/noneToZero.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/utils/pathMapping.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/utils/testing.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/tests/test_settings.py +0 -0
- {generalmanager-0.10.3 → generalmanager-0.10.4}/tests/test_urls.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: GeneralManager
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.4
|
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,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: GeneralManager
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.4
|
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
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "GeneralManager"
|
7
|
-
version = "0.10.
|
7
|
+
version = "0.10.4"
|
8
8
|
description = "Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching."
|
9
9
|
readme = "README.md"
|
10
10
|
authors = [{ name = "Tim Kleindick", email = "tkleindick@yahoo.de" }]
|
@@ -102,9 +102,9 @@ class GraphQL:
|
|
102
102
|
@classmethod
|
103
103
|
def createGraphqlInterface(cls, generalManagerClass: GeneralManagerMeta) -> None:
|
104
104
|
"""
|
105
|
-
Generates and registers a GraphQL ObjectType for
|
105
|
+
Generates and registers a GraphQL ObjectType for a given GeneralManager class.
|
106
106
|
|
107
|
-
This method maps interface
|
107
|
+
This method maps interface and GraphQLProperty attributes to Graphene fields, creates resolvers for each field, registers the resulting type in the internal registry, and adds corresponding query fields to the schema.
|
108
108
|
"""
|
109
109
|
interface_cls: InterfaceBase | None = getattr(
|
110
110
|
generalManagerClass, "Interface", None
|
@@ -348,14 +348,7 @@ class GraphQL:
|
|
348
348
|
base_getter: Callable[[Any], Any], fallback_manager_class: type[GeneralManager]
|
349
349
|
) -> Callable[..., Any]:
|
350
350
|
"""
|
351
|
-
|
352
|
-
|
353
|
-
Parameters:
|
354
|
-
base_getter (Callable): Function to obtain the base queryset from the parent instance.
|
355
|
-
fallback_manager_class (type[GeneralManager]): Manager class to use if the queryset does not specify one.
|
356
|
-
|
357
|
-
Returns:
|
358
|
-
Callable: A resolver function for use in GraphQL list fields.
|
351
|
+
Returns a resolver function for GraphQL list fields that retrieves a queryset, applies permission-based filtering, query filters, sorting, pagination, and optional grouping, and returns the resulting data.
|
359
352
|
"""
|
360
353
|
|
361
354
|
def resolver(
|
@@ -370,19 +363,19 @@ class GraphQL:
|
|
370
363
|
group_by: list[str] | None = None,
|
371
364
|
) -> Any:
|
372
365
|
"""
|
373
|
-
Resolves a list field by
|
366
|
+
Resolves a list field by returning a queryset filtered by permissions, query parameters, sorting, pagination, and optional grouping.
|
374
367
|
|
375
368
|
Parameters:
|
376
|
-
filter:
|
377
|
-
exclude:
|
378
|
-
sort_by:
|
379
|
-
reverse:
|
380
|
-
page:
|
381
|
-
page_size:
|
382
|
-
group_by:
|
369
|
+
filter: Filter criteria as a dictionary or JSON string.
|
370
|
+
exclude: Exclusion criteria as a dictionary or JSON string.
|
371
|
+
sort_by: Field to sort by, as a Graphene Enum.
|
372
|
+
reverse: Whether to reverse the sort order.
|
373
|
+
page: Page number for pagination.
|
374
|
+
page_size: Number of items per page.
|
375
|
+
group_by: List of field names to group results by.
|
383
376
|
|
384
377
|
Returns:
|
385
|
-
|
378
|
+
A queryset with applied permission filters, filtering, sorting, pagination, and grouping.
|
386
379
|
"""
|
387
380
|
base_queryset = base_getter(self)
|
388
381
|
# use _manager_class from the attribute if available, otherwise fallback
|
@@ -460,7 +453,7 @@ class GraphQL:
|
|
460
453
|
"""
|
461
454
|
Adds list and single-item query fields for a GeneralManager-derived class to the GraphQL schema.
|
462
455
|
|
463
|
-
|
456
|
+
Registers a list query supporting filtering, sorting, pagination, and grouping, as well as a single-item query using identification fields for the specified manager class. The corresponding resolvers are attached to the schema.
|
464
457
|
"""
|
465
458
|
if not issubclass(generalManagerClass, GeneralManager):
|
466
459
|
raise TypeError(
|
@@ -526,10 +519,10 @@ class GraphQL:
|
|
526
519
|
"""
|
527
520
|
Generate a dictionary of Graphene input fields for mutations based on the attributes of the provided interface class.
|
528
521
|
|
529
|
-
Skips
|
522
|
+
Skips system-managed fields and derived attributes. For attributes referencing `GeneralManager` subclasses, uses ID or list of IDs as appropriate. Other types are mapped to their corresponding Graphene scalar types. Each field is annotated with an `editable` attribute. Adds an optional `history_comment` field marked as editable.
|
530
523
|
|
531
524
|
Returns:
|
532
|
-
dict[str, Any]:
|
525
|
+
dict[str, Any]: Mapping of attribute names to Graphene input fields for mutation arguments.
|
533
526
|
"""
|
534
527
|
fields: dict[str, Any] = {}
|
535
528
|
|
@@ -579,12 +572,12 @@ class GraphQL:
|
|
579
572
|
default_return_values: dict[str, Any],
|
580
573
|
) -> type[graphene.Mutation] | None:
|
581
574
|
"""
|
582
|
-
|
575
|
+
Dynamically generates a Graphene mutation class for creating an instance of the specified GeneralManager subclass.
|
583
576
|
|
584
|
-
The generated mutation class
|
577
|
+
The generated mutation class includes a `mutate` method that filters out fields with `NOT_PROVIDED` values, invokes the manager's `create` method with the provided arguments and the current user's ID, and returns a dictionary indicating success, any errors, and the created instance. Returns None if the manager class does not define an interface.
|
585
578
|
|
586
579
|
Returns:
|
587
|
-
The generated Graphene mutation class, or None if
|
580
|
+
The generated Graphene mutation class, or None if no interface is defined for the manager class.
|
588
581
|
"""
|
589
582
|
interface_cls: InterfaceBase | None = getattr(
|
590
583
|
generalManagerClass, "Interface", None
|
@@ -595,12 +588,12 @@ class GraphQL:
|
|
595
588
|
def create_mutation(
|
596
589
|
self,
|
597
590
|
info: GraphQLResolveInfo,
|
598
|
-
**kwargs:
|
591
|
+
**kwargs: Any,
|
599
592
|
) -> dict:
|
600
593
|
"""
|
601
|
-
Creates a new instance of the
|
594
|
+
Creates a new instance of the manager class with the provided arguments.
|
602
595
|
|
603
|
-
Filters out fields with
|
596
|
+
Filters out fields with values marked as `NOT_PROVIDED` before creation. Returns a dictionary containing a success flag, a list of errors (if any), and the created instance under a key named after the manager class.
|
604
597
|
"""
|
605
598
|
try:
|
606
599
|
kwargs = {
|
@@ -651,9 +644,9 @@ class GraphQL:
|
|
651
644
|
default_return_values: dict[str, Any],
|
652
645
|
) -> type[graphene.Mutation] | None:
|
653
646
|
"""
|
654
|
-
Generates a GraphQL mutation class for updating
|
647
|
+
Generates a GraphQL mutation class for updating instances of a GeneralManager subclass.
|
655
648
|
|
656
|
-
The generated mutation accepts editable fields as arguments,
|
649
|
+
The generated mutation accepts editable fields as arguments, invokes the manager's `update` method with the provided values, and returns a dictionary indicating success, any errors, and the updated instance. Returns None if the manager class does not define an `Interface`.
|
657
650
|
|
658
651
|
Returns:
|
659
652
|
The generated Graphene mutation class, or None if no interface is defined.
|
@@ -667,17 +660,17 @@ class GraphQL:
|
|
667
660
|
def update_mutation(
|
668
661
|
self,
|
669
662
|
info: GraphQLResolveInfo,
|
670
|
-
**kwargs:
|
663
|
+
**kwargs: Any,
|
671
664
|
) -> dict:
|
672
665
|
"""
|
673
|
-
|
666
|
+
Performs an update mutation on a GeneralManager instance with the specified fields.
|
674
667
|
|
675
668
|
Parameters:
|
676
|
-
info (GraphQLResolveInfo): GraphQL resolver context containing user and request information.
|
669
|
+
info (GraphQLResolveInfo): The GraphQL resolver context, typically containing user and request information.
|
677
670
|
**kwargs: Fields to update, including the required 'id' of the instance.
|
678
671
|
|
679
672
|
Returns:
|
680
|
-
dict:
|
673
|
+
dict: Contains the operation's success status, a list of error messages if any, and the updated instance keyed by its class name.
|
681
674
|
"""
|
682
675
|
try:
|
683
676
|
manager_id = kwargs.pop("id", None)
|
@@ -725,7 +718,7 @@ class GraphQL:
|
|
725
718
|
"""
|
726
719
|
Generates a GraphQL mutation class for deleting (deactivating) an instance of a GeneralManager subclass.
|
727
720
|
|
728
|
-
The generated mutation accepts input fields defined in the manager's interface, deactivates the specified instance, and returns a dictionary
|
721
|
+
The generated mutation accepts input fields defined in the manager's interface, deactivates the specified instance using its ID, and returns a dictionary with the operation's success status, any error messages, and the deactivated instance.
|
729
722
|
"""
|
730
723
|
interface_cls: InterfaceBase | None = getattr(
|
731
724
|
generalManagerClass, "Interface", None
|
@@ -736,13 +729,13 @@ class GraphQL:
|
|
736
729
|
def delete_mutation(
|
737
730
|
self,
|
738
731
|
info: GraphQLResolveInfo,
|
739
|
-
**kwargs:
|
732
|
+
**kwargs: Any,
|
740
733
|
) -> dict:
|
741
734
|
"""
|
742
|
-
|
735
|
+
Deactivates an instance of the specified GeneralManager class and returns the result.
|
743
736
|
|
744
737
|
Returns:
|
745
|
-
dict:
|
738
|
+
dict: Contains the operation's success status, a list of error messages if any, and the deactivated instance keyed by the class name.
|
746
739
|
"""
|
747
740
|
try:
|
748
741
|
manager_id = kwargs.pop("id", None)
|
@@ -43,12 +43,12 @@ class GeneralmanagerConfig(AppConfig):
|
|
43
43
|
|
44
44
|
@staticmethod
|
45
45
|
def handleReadOnlyInterface(
|
46
|
-
read_only_classes: list[Type[GeneralManager
|
46
|
+
read_only_classes: list[Type[GeneralManager]],
|
47
47
|
):
|
48
48
|
"""
|
49
|
-
Configures synchronization and schema validation for the
|
50
|
-
|
51
|
-
|
49
|
+
Configures synchronization and schema validation for the given read-only GeneralManager classes.
|
50
|
+
|
51
|
+
For each provided class, ensures that its data is synchronized before any Django management command executes, and registers a system check to verify that the associated schema remains up to date.
|
52
52
|
"""
|
53
53
|
GeneralmanagerConfig.patchReadOnlyInterfaceSync(read_only_classes)
|
54
54
|
from general_manager.interface.readOnlyInterface import ReadOnlyInterface
|
@@ -68,11 +68,11 @@ class GeneralmanagerConfig(AppConfig):
|
|
68
68
|
|
69
69
|
@staticmethod
|
70
70
|
def patchReadOnlyInterfaceSync(
|
71
|
-
general_manager_classes: list[Type[GeneralManager
|
71
|
+
general_manager_classes: list[Type[GeneralManager]],
|
72
72
|
):
|
73
73
|
"""
|
74
74
|
Monkey-patches Django's management command runner to synchronize all provided read-only interfaces before executing any management command, except during autoreload subprocesses of 'runserver'.
|
75
|
-
|
75
|
+
|
76
76
|
For each class in `general_manager_classes`, the associated read-only interface's `syncData` method is called prior to command execution, ensuring data consistency before management operations.
|
77
77
|
"""
|
78
78
|
from general_manager.interface.readOnlyInterface import ReadOnlyInterface
|
@@ -58,17 +58,14 @@ class Bucket(ABC, Generic[GeneralManagerType]):
|
|
58
58
|
@abstractmethod
|
59
59
|
def __or__(
|
60
60
|
self,
|
61
|
-
other:
|
62
|
-
Bucket[GeneralManagerType]
|
63
|
-
| GeneralManager[GeneralManagerType, InterfaceBase]
|
64
|
-
),
|
61
|
+
other: Bucket[GeneralManagerType] | GeneralManagerType,
|
65
62
|
) -> Bucket[GeneralManagerType]:
|
66
63
|
"""
|
67
|
-
|
68
|
-
|
69
|
-
|
64
|
+
Return a new bucket containing the union of this bucket and another bucket or manager instance.
|
65
|
+
|
66
|
+
Parameters:
|
70
67
|
other: Another bucket or a single manager instance to combine with this bucket.
|
71
|
-
|
68
|
+
|
72
69
|
Returns:
|
73
70
|
A new bucket containing all unique items from both sources.
|
74
71
|
"""
|
{generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/bucket/calculationBucket.py
RENAMED
@@ -92,19 +92,18 @@ class CalculationBucket(Bucket[GeneralManagerType]):
|
|
92
92
|
|
93
93
|
def __or__(
|
94
94
|
self,
|
95
|
-
other:
|
96
|
-
Bucket[GeneralManagerType]
|
97
|
-
| GeneralManager[GeneralManagerType, CalculationInterface]
|
98
|
-
),
|
95
|
+
other: Bucket[GeneralManagerType] | GeneralManagerType,
|
99
96
|
) -> CalculationBucket[GeneralManagerType]:
|
100
97
|
"""
|
101
|
-
|
102
|
-
|
103
|
-
If combined with a manager instance, returns a bucket filtered to that manager's identification.
|
104
|
-
|
105
|
-
|
98
|
+
Combine this CalculationBucket with another bucket or manager instance of the same manager class.
|
99
|
+
|
100
|
+
If combined with a manager instance, returns a bucket filtered to that manager'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
|
+
|
106
102
|
Raises:
|
107
103
|
ValueError: If the other object is not a CalculationBucket or manager of the same class.
|
104
|
+
|
105
|
+
Returns:
|
106
|
+
CalculationBucket[GeneralManagerType]: A new CalculationBucket representing the intersection of filters and excludes, or a filtered bucket for the given manager instance.
|
108
107
|
"""
|
109
108
|
from general_manager.manager.generalManager import GeneralManager
|
110
109
|
|
{generalmanager-0.10.3 → generalmanager-0.10.4}/src/general_manager/bucket/databaseBucket.py
RENAMED
@@ -1,11 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import
|
3
|
-
Type,
|
4
|
-
Any,
|
5
|
-
Generator,
|
6
|
-
TypeVar,
|
7
|
-
TYPE_CHECKING,
|
8
|
-
)
|
2
|
+
from typing import Type, Any, Generator, TypeVar, TYPE_CHECKING
|
9
3
|
from django.db import models
|
10
4
|
from general_manager.interface.baseInterface import (
|
11
5
|
GeneralManagerType,
|
@@ -30,16 +24,15 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
30
24
|
exclude_definitions: dict[str, list[Any]] | None = None,
|
31
25
|
):
|
32
26
|
"""
|
33
|
-
|
27
|
+
Initialize a DatabaseBucket with a Django queryset, a manager class, and optional filter and exclude definitions.
|
34
28
|
|
35
|
-
|
29
|
+
Parameters:
|
30
|
+
data (QuerySet): The Django queryset containing model instances to be managed.
|
31
|
+
manager_class (Type[GeneralManagerType]): The manager class used to wrap model instances.
|
32
|
+
filter_definitions (dict[str, list[Any]], optional): Initial filter criteria for the queryset.
|
33
|
+
exclude_definitions (dict[str, list[Any]], optional): Initial exclude criteria for the queryset.
|
36
34
|
"""
|
37
|
-
if data is None:
|
38
|
-
data = manager_class.Interface._model.objects.filter(
|
39
|
-
**filter_definitions
|
40
|
-
).exclude(**exclude_definitions)
|
41
35
|
self._data = data
|
42
|
-
|
43
36
|
self._manager_class = manager_class
|
44
37
|
self.filters = {**(filter_definitions or {})}
|
45
38
|
self.excludes = {**(exclude_definitions or {})}
|
@@ -55,18 +48,15 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
55
48
|
|
56
49
|
def __or__(
|
57
50
|
self,
|
58
|
-
other:
|
59
|
-
Bucket[GeneralManagerType]
|
60
|
-
| GeneralManager[GeneralManagerType, DatabaseInterface]
|
61
|
-
),
|
51
|
+
other: Bucket[GeneralManagerType] | GeneralManagerType,
|
62
52
|
) -> DatabaseBucket[GeneralManagerType]:
|
63
53
|
"""
|
64
|
-
|
54
|
+
Return a new bucket containing the union of this bucket and another bucket or manager instance.
|
65
55
|
|
66
|
-
If `other` is a manager instance of the same class,
|
56
|
+
If `other` is a manager instance of the same class, it is converted to a bucket before combining. If `other` is a compatible bucket, the resulting bucket contains all unique items from both. Raises a `ValueError` if the types or manager classes are incompatible.
|
67
57
|
|
68
58
|
Returns:
|
69
|
-
A new
|
59
|
+
DatabaseBucket[GeneralManagerType]: A new bucket with the combined items.
|
70
60
|
"""
|
71
61
|
if isinstance(other, GeneralManager) and other.__class__ == self._manager_class:
|
72
62
|
return self.__or__(
|
@@ -202,10 +192,7 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
202
192
|
|
203
193
|
def __contains__(self, item: GeneralManagerType | models.Model) -> bool:
|
204
194
|
"""
|
205
|
-
|
206
|
-
|
207
|
-
Args:
|
208
|
-
item: A manager instance or Django model instance to check for membership.
|
195
|
+
Determine whether a manager instance or Django model instance is present in the bucket.
|
209
196
|
|
210
197
|
Returns:
|
211
198
|
True if the item's primary key exists in the underlying queryset; otherwise, False.
|
@@ -216,7 +203,7 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
216
203
|
return item.identification.get("id", None) in self._data.values_list(
|
217
204
|
"pk", flat=True
|
218
205
|
)
|
219
|
-
return item in self._data
|
206
|
+
return item.pk in self._data.values_list("pk", flat=True)
|
220
207
|
|
221
208
|
def sort(
|
222
209
|
self,
|
@@ -224,14 +211,14 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
224
211
|
reverse: bool = False,
|
225
212
|
) -> DatabaseBucket:
|
226
213
|
"""
|
227
|
-
|
214
|
+
Return a new DatabaseBucket sorted by the specified field or fields.
|
228
215
|
|
229
|
-
|
230
|
-
key:
|
231
|
-
reverse: If True,
|
216
|
+
Parameters:
|
217
|
+
key (str or tuple of str): Field name or tuple of field names to sort by.
|
218
|
+
reverse (bool): If True, sort in descending order.
|
232
219
|
|
233
220
|
Returns:
|
234
|
-
A new
|
221
|
+
DatabaseBucket: A new bucket instance containing the sorted queryset.
|
235
222
|
"""
|
236
223
|
if isinstance(key, str):
|
237
224
|
key = (key,)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
from django.dispatch import Signal
|
2
|
+
from typing import Callable, TypeVar, ParamSpec, cast
|
3
|
+
|
4
|
+
from functools import wraps
|
5
|
+
|
6
|
+
post_data_change = Signal()
|
7
|
+
|
8
|
+
pre_data_change = Signal()
|
9
|
+
|
10
|
+
P = ParamSpec("P")
|
11
|
+
R = TypeVar("R")
|
12
|
+
|
13
|
+
|
14
|
+
def dataChange(func: Callable[P, R]) -> Callable[P, R]:
|
15
|
+
"""
|
16
|
+
Decorator that emits pre- and post-data change signals around the execution of the decorated function.
|
17
|
+
|
18
|
+
Sends the `pre_data_change` signal before the wrapped function is called and the `post_data_change` signal after it completes. The signals include information about the sender, action, and relevant instance state before and after the change. Handles both regular functions and classmethods. Intended for use with functions that modify data to enable signal-based hooks for data change events.
|
19
|
+
"""
|
20
|
+
|
21
|
+
@wraps(func)
|
22
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
23
|
+
"""
|
24
|
+
Wraps a function to emit pre- and post-data change signals around its execution.
|
25
|
+
|
26
|
+
Sends the `pre_data_change` signal before the wrapped function is called and the `post_data_change` signal after, providing context such as the sender, action name, and relevant instance data. Handles both regular functions and classmethods, and distinguishes the "create" action by omitting a pre-existing instance.
|
27
|
+
"""
|
28
|
+
action = func.__name__
|
29
|
+
if func.__name__ == "create":
|
30
|
+
sender = args[0]
|
31
|
+
instance_before = None
|
32
|
+
else:
|
33
|
+
instance = args[0]
|
34
|
+
sender = instance.__class__
|
35
|
+
instance_before = instance
|
36
|
+
pre_data_change.send(
|
37
|
+
sender=sender,
|
38
|
+
instance=instance_before,
|
39
|
+
action=action,
|
40
|
+
**kwargs,
|
41
|
+
)
|
42
|
+
old_relevant_values = getattr(instance_before, "_old_values", {})
|
43
|
+
if isinstance(func, classmethod):
|
44
|
+
inner = cast(Callable[P, R], func.__func__)
|
45
|
+
result = inner(*args, **kwargs)
|
46
|
+
else:
|
47
|
+
result = func(*args, **kwargs)
|
48
|
+
|
49
|
+
instance = result
|
50
|
+
|
51
|
+
post_data_change.send(
|
52
|
+
sender=sender,
|
53
|
+
instance=instance,
|
54
|
+
action=action,
|
55
|
+
old_relevant_values=old_relevant_values,
|
56
|
+
**kwargs,
|
57
|
+
)
|
58
|
+
return result
|
59
|
+
|
60
|
+
return wrapper
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Type, Callable, Union, Any, TypeVar, Literal
|
|
3
3
|
from django.db import models
|
4
4
|
from factory.django import DjangoModelFactory
|
5
5
|
from general_manager.factory.factories import getFieldValue, getManyToManyFieldValue
|
6
|
-
|
6
|
+
from django.contrib.contenttypes.fields import GenericForeignKey
|
7
7
|
|
8
8
|
if TYPE_CHECKING:
|
9
9
|
from general_manager.interface.databaseInterface import (
|
@@ -29,16 +29,16 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
29
29
|
cls, strategy: Literal["build", "create"], params: dict[str, Any]
|
30
30
|
) -> models.Model | list[models.Model]:
|
31
31
|
"""
|
32
|
-
Generates and populates
|
32
|
+
Generates and populates one or more Django model instances with automatic field value assignment.
|
33
33
|
|
34
|
-
Automatically
|
34
|
+
Automatically fills unset model fields, excluding generic foreign keys and auto-created fields, and handles custom and special fields as defined by the interface. After instance creation or building, processes many-to-many relationships. Raises a ValueError if the model is not a subclass of Django's Model.
|
35
35
|
|
36
|
-
|
37
|
-
strategy:
|
38
|
-
params: Field values to use for instance generation; missing fields are auto-filled.
|
36
|
+
Parameters:
|
37
|
+
strategy (Literal["build", "create"]): Determines whether to build (unsaved) or create (saved) the instance(s).
|
38
|
+
params (dict[str, Any]): Field values to use for instance generation; missing fields are auto-filled.
|
39
39
|
|
40
40
|
Returns:
|
41
|
-
|
41
|
+
models.Model or list[models.Model]: The generated model instance or list of instances.
|
42
42
|
"""
|
43
43
|
cls._original_params = params
|
44
44
|
model = cls._meta.model
|
@@ -50,6 +50,7 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
50
50
|
field
|
51
51
|
for field in model._meta.get_fields()
|
52
52
|
if field.name not in to_ignore_list
|
53
|
+
and not isinstance(field, GenericForeignKey)
|
53
54
|
]
|
54
55
|
special_fields: list[models.Field[Any, Any]] = [
|
55
56
|
getattr(model, field_name) for field_name in field_name_list
|
@@ -72,7 +73,7 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
72
73
|
|
73
74
|
obj: list[models.Model] | models.Model = super()._generate(strategy, params)
|
74
75
|
if isinstance(obj, list):
|
75
|
-
for item in obj:
|
76
|
+
for item in obj:
|
76
77
|
if not isinstance(item, models.Model):
|
77
78
|
raise ValueError("Model must be a type")
|
78
79
|
cls._handleManyToManyFieldsAfterCreation(item, params)
|
@@ -85,9 +86,9 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
85
86
|
cls, obj: models.Model, attrs: dict[str, Any]
|
86
87
|
) -> None:
|
87
88
|
"""
|
88
|
-
|
89
|
+
Assigns related objects to all many-to-many fields of a Django model instance after it has been created.
|
89
90
|
|
90
|
-
For each many-to-many field,
|
91
|
+
For each many-to-many field, sets the related objects from the provided attributes if available; otherwise, generates related objects automatically. Uses the Django ORM's `set` method to establish the relationships.
|
91
92
|
"""
|
92
93
|
for field in obj._meta.many_to_many:
|
93
94
|
if field.name in attrs:
|
@@ -101,10 +102,10 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
101
102
|
def _adjust_kwargs(cls, **kwargs: dict[str, Any]) -> dict[str, Any]:
|
102
103
|
# Remove ManyToMany fields from kwargs before object creation
|
103
104
|
"""
|
104
|
-
Removes many-to-many
|
105
|
+
Removes many-to-many fields from the provided keyword arguments before creating or building a model instance.
|
105
106
|
|
106
107
|
Returns:
|
107
|
-
The keyword arguments
|
108
|
+
dict[str, Any]: The keyword arguments with any many-to-many fields excluded.
|
108
109
|
"""
|
109
110
|
model: Type[models.Model] = cls._meta.model
|
110
111
|
m2m_fields = {field.name for field in model._meta.many_to_many}
|
@@ -117,15 +118,12 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
117
118
|
cls, model_class: Type[models.Model], *args: list[Any], **kwargs: dict[str, Any]
|
118
119
|
) -> models.Model | list[models.Model]:
|
119
120
|
"""
|
120
|
-
|
121
|
-
|
122
|
-
If an adjustment method is defined, it is used to generate or modify field values before creation. Otherwise, the model is instantiated and saved with the provided attributes.
|
121
|
+
Create and save a Django model instance or multiple instances, optionally applying custom adjustment logic to field values.
|
123
122
|
|
124
|
-
|
125
|
-
model_class: The Django model class to instantiate.
|
123
|
+
If an adjustment method is defined, it is used to generate or modify field values before instance creation. Otherwise, the model is instantiated and saved with the provided attributes.
|
126
124
|
|
127
125
|
Returns:
|
128
|
-
A saved model instance or a list of instances.
|
126
|
+
A saved model instance or a list of saved instances.
|
129
127
|
"""
|
130
128
|
kwargs = cls._adjust_kwargs(**kwargs)
|
131
129
|
if cls._adjustmentMethod is not None:
|
@@ -137,9 +135,12 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
137
135
|
cls, model_class: Type[models.Model], *args: list[Any], **kwargs: dict[str, Any]
|
138
136
|
) -> models.Model | list[models.Model]:
|
139
137
|
"""
|
140
|
-
|
138
|
+
Builds an unsaved instance or list of instances of the specified Django model class.
|
141
139
|
|
142
|
-
If an adjustment method is defined, it is used to generate or modify field values before building
|
140
|
+
If an adjustment method is defined, it is used to generate or modify field values before building. Many-to-many fields are excluded from the keyword arguments prior to instantiation.
|
141
|
+
|
142
|
+
Returns:
|
143
|
+
models.Model or list[models.Model]: The unsaved model instance or list of instances.
|
143
144
|
"""
|
144
145
|
kwargs = cls._adjust_kwargs(**kwargs)
|
145
146
|
if cls._adjustmentMethod is not None:
|
@@ -153,9 +154,9 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
153
154
|
cls, model_class: Type[models.Model], **kwargs: dict[str, Any]
|
154
155
|
) -> models.Model:
|
155
156
|
"""
|
156
|
-
|
157
|
+
Create, validate, and save a Django model instance with the specified field values.
|
157
158
|
|
158
|
-
Initializes the model, assigns attributes from keyword arguments,
|
159
|
+
Initializes the model, assigns attributes from keyword arguments, performs validation with `full_clean()`, and saves the instance to the database.
|
159
160
|
|
160
161
|
Returns:
|
161
162
|
The saved Django model instance.
|
@@ -172,14 +173,14 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
172
173
|
cls, model_class: Type[models.Model], **kwargs: dict[str, Any]
|
173
174
|
) -> models.Model:
|
174
175
|
"""
|
175
|
-
Constructs an unsaved instance
|
176
|
+
Constructs an unsaved Django model instance with the specified field values.
|
176
177
|
|
177
|
-
|
178
|
-
model_class: The Django model class to instantiate.
|
179
|
-
**kwargs: Field values to
|
178
|
+
Parameters:
|
179
|
+
model_class (Type[models.Model]): The Django model class to instantiate.
|
180
|
+
**kwargs: Field values to assign to the model instance.
|
180
181
|
|
181
182
|
Returns:
|
182
|
-
An unsaved
|
183
|
+
models.Model: An unsaved instance of the specified model with attributes set from kwargs.
|
183
184
|
"""
|
184
185
|
obj = model_class()
|
185
186
|
for field, value in kwargs.items():
|
@@ -191,17 +192,16 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
191
192
|
cls, use_creation_method: bool, params: dict[str, Any]
|
192
193
|
) -> models.Model | list[models.Model]:
|
193
194
|
"""
|
194
|
-
|
195
|
+
Creates or builds one or more model instances using the adjustment method to generate field values.
|
195
196
|
|
196
|
-
If the adjustment method returns a single dictionary,
|
197
|
-
If it returns a list of dictionaries, creates or builds multiple instances accordingly.
|
197
|
+
If the adjustment method returns a single dictionary, a single instance is created or built. If it returns a list of dictionaries, multiple instances are created or built accordingly.
|
198
198
|
|
199
|
-
|
200
|
-
use_creation_method: If True,
|
201
|
-
params:
|
199
|
+
Parameters:
|
200
|
+
use_creation_method (bool): If True, instances are saved to the database; if False, instances are built but not saved.
|
201
|
+
params (dict[str, Any]): Arguments passed to the adjustment method for generating field values.
|
202
202
|
|
203
203
|
Returns:
|
204
|
-
|
204
|
+
models.Model or list[models.Model]: The created or built model instance(s).
|
205
205
|
|
206
206
|
Raises:
|
207
207
|
ValueError: If the adjustment method is not defined.
|