statezero 0.1.0b17__tar.gz → 0.1.0b18__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.
Potentially problematic release.
This version of statezero might be problematic. Click here for more details.
- {statezero-0.1.0b17 → statezero-0.1.0b18}/PKG-INFO +1 -1
- {statezero-0.1.0b17 → statezero-0.1.0b18}/pyproject.toml +1 -1
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/orm.py +9 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/schemas.py +2 -2
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/serializers.py +33 -24
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/views.py +2 -2
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/core/ast_parser.py +2 -13
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/core/config.py +0 -2
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/core/interfaces.py +10 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero.egg-info/PKG-INFO +1 -1
- {statezero-0.1.0b17 → statezero-0.1.0b18}/README.md +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/license.md +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/requirements.txt +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/setup.cfg +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/__init__.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/__init__.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/__init__.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/actions.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/apps.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/config.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/context_manager.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/event_emitters.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/exception_handler.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/extensions/__init__.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/extensions/custom_field_serializers/__init__.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/extensions/custom_field_serializers/file_fields.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/extensions/custom_field_serializers/money_field.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/f_handler.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/helpers.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/middleware.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/migrations/0001_initial.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/migrations/0002_delete_modelviewsubscription.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/migrations/__init__.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/permissions.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/query_optimizer.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/search_providers/__init__.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/search_providers/basic_search.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/search_providers/postgres_search.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/urls.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/core/__init__.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/core/actions.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/core/ast_validator.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/core/classes.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/core/context_storage.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/core/event_bus.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/core/event_emitters.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/core/exceptions.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/core/hook_checks.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/core/process_request.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/core/types.py +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero.egg-info/SOURCES.txt +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero.egg-info/dependency_links.txt +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero.egg-info/requires.txt +0 -0
- {statezero-0.1.0b17 → statezero-0.1.0b18}/statezero.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: statezero
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.0b18
|
|
4
4
|
Summary: Connect your Python backend to a modern JavaScript SPA frontend with 90% less complexity.
|
|
5
5
|
Author-email: Robert <robert.herring@statezero.dev>
|
|
6
6
|
Project-URL: homepage, https://www.statezero.dev
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "statezero"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.0b18"
|
|
8
8
|
description = "Connect your Python backend to a modern JavaScript SPA frontend with 90% less complexity."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -763,6 +763,7 @@ class DjangoORMAdapter(AbstractORMProvider):
|
|
|
763
763
|
def get_fields(self, model: models.Model) -> Set[str]:
|
|
764
764
|
"""
|
|
765
765
|
Return a set of the model fields.
|
|
766
|
+
Includes both database fields and additional_fields (computed fields).
|
|
766
767
|
"""
|
|
767
768
|
model_config = registry.get_config(model)
|
|
768
769
|
if model_config.fields and "__all__" != model_config.fields:
|
|
@@ -775,6 +776,14 @@ class DjangoORMAdapter(AbstractORMProvider):
|
|
|
775
776
|
resolved_fields = resolved_fields.union(additional_fields)
|
|
776
777
|
return resolved_fields
|
|
777
778
|
|
|
779
|
+
def get_db_fields(self, model: models.Model) -> Set[str]:
|
|
780
|
+
"""
|
|
781
|
+
Return only actual database fields for the model.
|
|
782
|
+
Excludes read-only additional_fields (computed fields).
|
|
783
|
+
Used for deserialization - hooks can write to any DB field.
|
|
784
|
+
"""
|
|
785
|
+
return set(field.name for field in model._meta.get_fields())
|
|
786
|
+
|
|
778
787
|
def build_model_graph(
|
|
779
788
|
self, model: Type[models.Model], model_graph: nx.DiGraph = None
|
|
780
789
|
) -> nx.DiGraph:
|
|
@@ -41,9 +41,9 @@ class DjangoSchemaGenerator(AbstractSchemaGenerator):
|
|
|
41
41
|
all_field_names: Set[str] = set()
|
|
42
42
|
db_field_names: Set[str] = set()
|
|
43
43
|
|
|
44
|
-
if model_config.
|
|
44
|
+
if model_config.fields != "__all__":
|
|
45
45
|
all_fields = [
|
|
46
|
-
field for field in all_fields if field.name in model_config.
|
|
46
|
+
field for field in all_fields if field.name in model_config.fields
|
|
47
47
|
]
|
|
48
48
|
|
|
49
49
|
for field in all_fields:
|
|
@@ -6,7 +6,7 @@ from rest_framework import serializers
|
|
|
6
6
|
import contextvars
|
|
7
7
|
from contextlib import contextmanager
|
|
8
8
|
import logging
|
|
9
|
-
from cytoolz import pluck
|
|
9
|
+
from cytoolz import pluck, keyfilter
|
|
10
10
|
from zen_queries import queries_disabled
|
|
11
11
|
|
|
12
12
|
from statezero.adaptors.django.config import config, registry
|
|
@@ -484,33 +484,42 @@ class DRFDynamicSerializer(AbstractDataSerializer):
|
|
|
484
484
|
# Serious security issue if fields_map is None
|
|
485
485
|
assert fields_map is not None, "fields_map is required and cannot be None"
|
|
486
486
|
|
|
487
|
-
#
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
serializer_class = DynamicModelSerializer.for_model(model)
|
|
491
|
-
available_fields = set(serializer_class().fields.keys())
|
|
487
|
+
# Get model name and allowed fields from fields_map
|
|
488
|
+
model_name = config.orm_provider.get_model_name(model)
|
|
489
|
+
allowed_fields = fields_map.get(model_name, set())
|
|
492
490
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
491
|
+
# Filter user input to only allowed fields (security boundary)
|
|
492
|
+
data = dict(keyfilter(lambda k: k in allowed_fields, data))
|
|
493
|
+
|
|
494
|
+
try:
|
|
495
|
+
model_config = registry.get_config(model)
|
|
496
|
+
except ValueError:
|
|
497
|
+
# No model config available
|
|
498
|
+
model_config = None
|
|
499
|
+
|
|
500
|
+
# Run pre-hooks on filtered data (hooks can add any DB fields)
|
|
501
|
+
if model_config and model_config.pre_hooks:
|
|
502
|
+
for hook in model_config.pre_hooks:
|
|
503
|
+
hook_result = hook(data, request=request)
|
|
504
|
+
if settings.DEBUG:
|
|
505
|
+
# Note: available_fields check removed since hooks can add any DB field
|
|
506
|
+
data = hook_result or data
|
|
507
|
+
else:
|
|
508
|
+
data = hook_result or data
|
|
509
|
+
|
|
510
|
+
# Expand fields_map to all DB fields for serializer validation
|
|
511
|
+
# This allows hooks to add fields that aren't in the original fields_map
|
|
512
|
+
all_db_fields = config.orm_provider.get_db_fields(model)
|
|
513
|
+
expanded_fields_map = {model_name: all_db_fields}
|
|
514
|
+
|
|
515
|
+
# Use the context manager with expanded fields map
|
|
516
|
+
with fields_map_context(expanded_fields_map):
|
|
517
|
+
# Create serializer class with all DB fields available
|
|
518
|
+
serializer_class = DynamicModelSerializer.for_model(model)
|
|
510
519
|
|
|
511
520
|
# Create serializer
|
|
512
521
|
serializer = serializer_class(
|
|
513
|
-
data=data,
|
|
522
|
+
data=data,
|
|
514
523
|
partial=partial,
|
|
515
524
|
request=request
|
|
516
525
|
)
|
|
@@ -570,11 +570,11 @@ class FieldPermissionsView(APIView):
|
|
|
570
570
|
# For read operations, default "__all__" to frontend_fields
|
|
571
571
|
if operation_type == "read":
|
|
572
572
|
# If frontend_fields is also "__all__", then return all fields
|
|
573
|
-
if model_config.
|
|
573
|
+
if model_config.fields == "__all__":
|
|
574
574
|
return all_fields
|
|
575
575
|
# Otherwise, use frontend_fields as the default for "__all__"
|
|
576
576
|
else:
|
|
577
|
-
fields = model_config.
|
|
577
|
+
fields = model_config.fields
|
|
578
578
|
fields &= all_fields # Ensure fields actually exist
|
|
579
579
|
allowed_fields |= fields
|
|
580
580
|
else:
|
|
@@ -386,19 +386,8 @@ class ASTParser:
|
|
|
386
386
|
|
|
387
387
|
# If any permission allows all fields
|
|
388
388
|
if fields == "__all__":
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
# If frontend_fields is also "__all__", then return all fields
|
|
392
|
-
if model_config.frontend_fields == "__all__":
|
|
393
|
-
return all_fields
|
|
394
|
-
# Otherwise, use frontend_fields as the default for "__all__"
|
|
395
|
-
else:
|
|
396
|
-
fields = model_config.frontend_fields
|
|
397
|
-
fields &= all_fields # Ensure fields actually exist
|
|
398
|
-
allowed_fields |= fields
|
|
399
|
-
else:
|
|
400
|
-
# For create/update operations, "__all__" means truly all fields
|
|
401
|
-
return all_fields
|
|
389
|
+
return all_fields
|
|
390
|
+
|
|
402
391
|
# Add allowed fields from this permission
|
|
403
392
|
else: # Ensure we're not operating on the string "__all__"
|
|
404
393
|
fields &= all_fields # Ensure fields actually exist
|
|
@@ -181,7 +181,6 @@ class ModelConfig:
|
|
|
181
181
|
searchable_fields: Optional[Union[Set[str], Literal["__all__"]]] = None,
|
|
182
182
|
ordering_fields: Optional[Union[Set[str], Literal["__all__"]]] = None,
|
|
183
183
|
fields: Optional[Union[Set[str], Literal["__all__"]]] = None,
|
|
184
|
-
frontend_fields: Optional[Union[Set[str], Literal["__all__"]]] = None,
|
|
185
184
|
display: Optional[Any] = None,
|
|
186
185
|
DEBUG: bool = False,
|
|
187
186
|
):
|
|
@@ -196,7 +195,6 @@ class ModelConfig:
|
|
|
196
195
|
self.searchable_fields = searchable_fields or set()
|
|
197
196
|
self.ordering_fields = ordering_fields or set()
|
|
198
197
|
self.fields = fields or "__all__"
|
|
199
|
-
self.frontend_fields = frontend_fields or self.fields
|
|
200
198
|
self.display = display
|
|
201
199
|
self.DEBUG = DEBUG or False
|
|
202
200
|
|
|
@@ -70,6 +70,16 @@ class AbstractORMProvider(ABC):
|
|
|
70
70
|
def get_fields(self, model: ORMModel) -> Set[str]:
|
|
71
71
|
"""
|
|
72
72
|
Get all of the model fields - doesn't apply permissions check.
|
|
73
|
+
Includes both database fields and additional_fields (computed fields).
|
|
74
|
+
"""
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
@abstractmethod
|
|
78
|
+
def get_db_fields(self, model: ORMModel) -> Set[str]:
|
|
79
|
+
"""
|
|
80
|
+
Get only the actual database fields for a model.
|
|
81
|
+
Excludes read-only additional_fields (computed fields).
|
|
82
|
+
Used for deserialization - hooks can write to any DB field.
|
|
73
83
|
"""
|
|
74
84
|
pass
|
|
75
85
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: statezero
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.0b18
|
|
4
4
|
Summary: Connect your Python backend to a modern JavaScript SPA frontend with 90% less complexity.
|
|
5
5
|
Author-email: Robert <robert.herring@statezero.dev>
|
|
6
6
|
Project-URL: homepage, https://www.statezero.dev
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/migrations/0001_initial.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/search_providers/__init__.py
RENAMED
|
File without changes
|
{statezero-0.1.0b17 → statezero-0.1.0b18}/statezero/adaptors/django/search_providers/basic_search.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|