statezero 0.1.0b3__py3-none-any.whl → 0.1.0b5__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.
Potentially problematic release.
This version of statezero might be problematic. Click here for more details.
- statezero/adaptors/django/orm.py +224 -174
- statezero/adaptors/django/serializers.py +45 -26
- statezero/core/ast_parser.py +315 -175
- statezero/core/hook_checks.py +86 -0
- statezero/core/interfaces.py +193 -69
- statezero/core/process_request.py +1 -1
- {statezero-0.1.0b3.dist-info → statezero-0.1.0b5.dist-info}/METADATA +1 -1
- {statezero-0.1.0b3.dist-info → statezero-0.1.0b5.dist-info}/RECORD +11 -10
- {statezero-0.1.0b3.dist-info → statezero-0.1.0b5.dist-info}/WHEEL +0 -0
- {statezero-0.1.0b3.dist-info → statezero-0.1.0b5.dist-info}/licenses/license.md +0 -0
- {statezero-0.1.0b3.dist-info → statezero-0.1.0b5.dist-info}/top_level.txt +0 -0
|
@@ -13,6 +13,7 @@ from statezero.adaptors.django.config import config, registry
|
|
|
13
13
|
from statezero.core.interfaces import AbstractDataSerializer, AbstractQueryOptimizer
|
|
14
14
|
from statezero.core.types import RequestType
|
|
15
15
|
from statezero.adaptors.django.helpers import collect_from_queryset
|
|
16
|
+
from statezero.core.hook_checks import _check_pre_hook_result, _check_post_hook_result
|
|
16
17
|
|
|
17
18
|
logger = logging.getLogger(__name__)
|
|
18
19
|
|
|
@@ -82,7 +83,7 @@ class FlexiblePrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
|
|
|
82
83
|
|
|
83
84
|
# Otherwise, use the standard to_internal_value
|
|
84
85
|
return super().to_internal_value(data)
|
|
85
|
-
|
|
86
|
+
|
|
86
87
|
class FExpressionMixin:
|
|
87
88
|
"""
|
|
88
89
|
A mixin that can handle F expression objects in serializer write operations.
|
|
@@ -344,7 +345,7 @@ class DRFDynamicSerializer(AbstractDataSerializer):
|
|
|
344
345
|
fields_per_model=fields_map,
|
|
345
346
|
get_model_name_func=config.orm_provider.get_model_name,
|
|
346
347
|
)
|
|
347
|
-
|
|
348
|
+
|
|
348
349
|
if "requested-fields::" in fields_map:
|
|
349
350
|
requested_fields = fields_map["requested-fields::"]
|
|
350
351
|
data = query_optimizer.optimize(
|
|
@@ -359,7 +360,7 @@ class DRFDynamicSerializer(AbstractDataSerializer):
|
|
|
359
360
|
logger.debug(f"Query optimized for {model.__name__} with no explicit field selection")
|
|
360
361
|
except Exception as e:
|
|
361
362
|
logger.error(f"Error optimizing query for {model.__name__}: {e}")
|
|
362
|
-
|
|
363
|
+
|
|
363
364
|
return data
|
|
364
365
|
|
|
365
366
|
def serialize(
|
|
@@ -385,7 +386,7 @@ class DRFDynamicSerializer(AbstractDataSerializer):
|
|
|
385
386
|
"""
|
|
386
387
|
# Validate fields_map
|
|
387
388
|
assert fields_map is not None, "fields_map is required and cannot be None"
|
|
388
|
-
|
|
389
|
+
|
|
389
390
|
# Handle None data
|
|
390
391
|
if data is None:
|
|
391
392
|
return {
|
|
@@ -393,10 +394,10 @@ class DRFDynamicSerializer(AbstractDataSerializer):
|
|
|
393
394
|
"included": {},
|
|
394
395
|
"model_name": None
|
|
395
396
|
}
|
|
396
|
-
|
|
397
|
+
|
|
397
398
|
# Apply query optimization
|
|
398
399
|
data = self._optimize_queryset(data, model, fields_map)
|
|
399
|
-
|
|
400
|
+
|
|
400
401
|
# Use the fields_map context for all operations
|
|
401
402
|
with fields_map_context(fields_map):
|
|
402
403
|
# Collect all model instances based on the fields_map
|
|
@@ -406,7 +407,7 @@ class DRFDynamicSerializer(AbstractDataSerializer):
|
|
|
406
407
|
get_model_name=config.orm_provider.get_model_name,
|
|
407
408
|
get_model=config.orm_provider.get_model_by_name
|
|
408
409
|
)
|
|
409
|
-
|
|
410
|
+
|
|
410
411
|
# Extract primary keys for the top-level model
|
|
411
412
|
model_name = config.orm_provider.get_model_name(model)
|
|
412
413
|
pk_field = model._meta.pk.name
|
|
@@ -418,7 +419,7 @@ class DRFDynamicSerializer(AbstractDataSerializer):
|
|
|
418
419
|
"included": {},
|
|
419
420
|
"model_name": model_name
|
|
420
421
|
}
|
|
421
|
-
|
|
422
|
+
|
|
422
423
|
# For QuerySets, gather all instances
|
|
423
424
|
if isinstance(data, models.QuerySet):
|
|
424
425
|
top_level_instances = list(data)
|
|
@@ -428,26 +429,26 @@ class DRFDynamicSerializer(AbstractDataSerializer):
|
|
|
428
429
|
# For many=True with a list of instances
|
|
429
430
|
elif many and isinstance(data, list):
|
|
430
431
|
top_level_instances = [item for item in data if isinstance(item, model)]
|
|
431
|
-
|
|
432
|
+
|
|
432
433
|
# Extract primary keys for top-level instances
|
|
433
434
|
result["data"] = [getattr(instance, pk_field) for instance in top_level_instances]
|
|
434
|
-
|
|
435
|
+
|
|
435
436
|
# Apply zen-queries protection if configured
|
|
436
437
|
query_protection = getattr(settings, 'ZEN_STRICT_SERIALIZATION', False)
|
|
437
|
-
|
|
438
|
+
|
|
438
439
|
# Serialize each group of models
|
|
439
440
|
for model_type, instances in collected_models.items():
|
|
440
441
|
# Skip empty collections
|
|
441
442
|
if not instances:
|
|
442
443
|
continue
|
|
443
|
-
|
|
444
|
+
|
|
444
445
|
try:
|
|
445
446
|
# Get the model class for this type
|
|
446
447
|
model_class = config.orm_provider.get_model_by_name(model_type)
|
|
447
|
-
|
|
448
|
+
|
|
448
449
|
# Create a serializer for this model type
|
|
449
450
|
serializer_class = DynamicModelSerializer.for_model(model_class)
|
|
450
|
-
|
|
451
|
+
|
|
451
452
|
# Apply zen-queries protection if configured
|
|
452
453
|
if query_protection:
|
|
453
454
|
with queries_disabled():
|
|
@@ -461,15 +462,15 @@ class DRFDynamicSerializer(AbstractDataSerializer):
|
|
|
461
462
|
# [{pk: 1, ...}, {pk: 2, ...}] -> {1: {...}, 2: {...}}
|
|
462
463
|
# Create a dictionary indexed by primary key for easy lookup in the frontend
|
|
463
464
|
pk_indexed_data = dict(zip(pluck(pk_field_name, serialized_data), serialized_data))
|
|
464
|
-
|
|
465
|
+
|
|
465
466
|
# Add the serialized data to the result
|
|
466
467
|
result["included"][model_type] = pk_indexed_data
|
|
467
|
-
|
|
468
|
+
|
|
468
469
|
except Exception as e:
|
|
469
470
|
logger.error(f"Error serializing {model_type}: {e}")
|
|
470
471
|
# Include an empty list for this model type to maintain the expected structure
|
|
471
472
|
result["included"][model_type] = {}
|
|
472
|
-
|
|
473
|
+
|
|
473
474
|
return result
|
|
474
475
|
|
|
475
476
|
def deserialize(
|
|
@@ -487,12 +488,22 @@ class DRFDynamicSerializer(AbstractDataSerializer):
|
|
|
487
488
|
with fields_map_context(fields_map):
|
|
488
489
|
# Create serializer class
|
|
489
490
|
serializer_class = DynamicModelSerializer.for_model(model)
|
|
491
|
+
available_fields = set(serializer_class().fields.keys())
|
|
490
492
|
|
|
491
493
|
try:
|
|
492
494
|
model_config = registry.get_config(model)
|
|
493
495
|
if model_config.pre_hooks:
|
|
494
496
|
for hook in model_config.pre_hooks:
|
|
495
|
-
|
|
497
|
+
hook_result = hook(data, request=request)
|
|
498
|
+
if settings.DEBUG:
|
|
499
|
+
data = _check_pre_hook_result(
|
|
500
|
+
original_data=data,
|
|
501
|
+
result_data=hook_result,
|
|
502
|
+
model=model,
|
|
503
|
+
serializer_fields=available_fields
|
|
504
|
+
)
|
|
505
|
+
else:
|
|
506
|
+
data = hook_result or data
|
|
496
507
|
except ValueError:
|
|
497
508
|
# No model config available
|
|
498
509
|
model_config = None
|
|
@@ -508,10 +519,18 @@ class DRFDynamicSerializer(AbstractDataSerializer):
|
|
|
508
519
|
|
|
509
520
|
if model_config and model_config.post_hooks:
|
|
510
521
|
for hook in model_config.post_hooks:
|
|
511
|
-
|
|
512
|
-
|
|
522
|
+
hook_result = hook(validated_data, request=request)
|
|
523
|
+
if settings.DEBUG:
|
|
524
|
+
validated_data = _check_post_hook_result(
|
|
525
|
+
original_data=validated_data,
|
|
526
|
+
result_data=hook_result,
|
|
527
|
+
model=model
|
|
528
|
+
)
|
|
529
|
+
else:
|
|
530
|
+
validated_data = hook_result or validated_data
|
|
531
|
+
|
|
513
532
|
return validated_data
|
|
514
|
-
|
|
533
|
+
|
|
515
534
|
def save(
|
|
516
535
|
self,
|
|
517
536
|
model: Type[models.Model],
|
|
@@ -533,12 +552,12 @@ class DRFDynamicSerializer(AbstractDataSerializer):
|
|
|
533
552
|
|
|
534
553
|
# Create an unrestricted fields map
|
|
535
554
|
unrestricted_fields_map = {model_name: all_fields}
|
|
536
|
-
|
|
555
|
+
|
|
537
556
|
# Use the context manager with the unrestricted fields map
|
|
538
557
|
with fields_map_context(unrestricted_fields_map):
|
|
539
558
|
# Create serializer class
|
|
540
559
|
serializer_class = DynamicModelSerializer.for_model(model)
|
|
541
|
-
|
|
560
|
+
|
|
542
561
|
# Create serializer
|
|
543
562
|
serializer = serializer_class(
|
|
544
563
|
instance=instance, # Will be None for creation
|
|
@@ -546,9 +565,9 @@ class DRFDynamicSerializer(AbstractDataSerializer):
|
|
|
546
565
|
partial=partial if instance else False, # partial only makes sense for updates
|
|
547
566
|
request=request
|
|
548
567
|
)
|
|
549
|
-
|
|
568
|
+
|
|
550
569
|
# Validate the data
|
|
551
570
|
serializer.is_valid(raise_exception=True)
|
|
552
|
-
|
|
571
|
+
|
|
553
572
|
# Save and return the instance
|
|
554
|
-
return serializer.save()
|
|
573
|
+
return serializer.save()
|