statezero 0.1.0b12__tar.gz → 0.1.0b14__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.
Files changed (54) hide show
  1. {statezero-0.1.0b12 → statezero-0.1.0b14}/PKG-INFO +1 -1
  2. {statezero-0.1.0b12 → statezero-0.1.0b14}/pyproject.toml +1 -1
  3. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/extensions/custom_field_serializers/money_field.py +8 -0
  4. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/query_optimizer.py +40 -2
  5. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/schemas.py +2 -2
  6. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/core/ast_parser.py +13 -2
  7. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/core/config.py +2 -0
  8. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero.egg-info/PKG-INFO +1 -1
  9. {statezero-0.1.0b12 → statezero-0.1.0b14}/README.md +0 -0
  10. {statezero-0.1.0b12 → statezero-0.1.0b14}/license.md +0 -0
  11. {statezero-0.1.0b12 → statezero-0.1.0b14}/requirements.txt +0 -0
  12. {statezero-0.1.0b12 → statezero-0.1.0b14}/setup.cfg +0 -0
  13. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/__init__.py +0 -0
  14. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/__init__.py +0 -0
  15. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/__init__.py +0 -0
  16. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/actions.py +0 -0
  17. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/apps.py +0 -0
  18. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/config.py +0 -0
  19. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/context_manager.py +0 -0
  20. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/event_emitters.py +0 -0
  21. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/exception_handler.py +0 -0
  22. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/extensions/__init__.py +0 -0
  23. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/extensions/custom_field_serializers/__init__.py +0 -0
  24. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/extensions/custom_field_serializers/file_fields.py +0 -0
  25. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/f_handler.py +0 -0
  26. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/helpers.py +0 -0
  27. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/middleware.py +0 -0
  28. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/migrations/0001_initial.py +0 -0
  29. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/migrations/0002_delete_modelviewsubscription.py +0 -0
  30. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/migrations/__init__.py +0 -0
  31. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/orm.py +0 -0
  32. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/permissions.py +0 -0
  33. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/search_providers/__init__.py +0 -0
  34. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/search_providers/basic_search.py +0 -0
  35. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/search_providers/postgres_search.py +0 -0
  36. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/serializers.py +0 -0
  37. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/urls.py +0 -0
  38. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/adaptors/django/views.py +0 -0
  39. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/core/__init__.py +0 -0
  40. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/core/actions.py +0 -0
  41. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/core/ast_validator.py +0 -0
  42. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/core/classes.py +0 -0
  43. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/core/context_storage.py +0 -0
  44. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/core/event_bus.py +0 -0
  45. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/core/event_emitters.py +0 -0
  46. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/core/exceptions.py +0 -0
  47. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/core/hook_checks.py +0 -0
  48. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/core/interfaces.py +0 -0
  49. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/core/process_request.py +0 -0
  50. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero/core/types.py +0 -0
  51. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero.egg-info/SOURCES.txt +0 -0
  52. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero.egg-info/dependency_links.txt +0 -0
  53. {statezero-0.1.0b12 → statezero-0.1.0b14}/statezero.egg-info/requires.txt +0 -0
  54. {statezero-0.1.0b12 → statezero-0.1.0b14}/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.0b12
3
+ Version: 0.1.0b14
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.0b12"
7
+ version = "0.1.0b14"
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" }
@@ -15,6 +15,14 @@ class MoneyFieldSerializer(serializers.Field):
15
15
  self.decimal_places = kwargs.pop("decimal_places", 2)
16
16
  super().__init__(**kwargs)
17
17
 
18
+ @classmethod
19
+ def get_prefetch_db_fields(cls, field_name: str):
20
+ """
21
+ Return all database fields required for this field to serialize.
22
+ MoneyField creates two database columns: field_name and field_name_currency.
23
+ """
24
+ return [field_name, f"{field_name}_currency"]
25
+
18
26
  def to_representation(self, value):
19
27
  djmoney_field = MoneyField(
20
28
  max_digits=self.max_digits, decimal_places=self.decimal_places
@@ -477,7 +477,34 @@ def optimize_query(queryset, fields=None, fields_map=None, depth=0, use_only=Tru
477
477
  related_fields_to_fetch = set()
478
478
 
479
479
  if fields_map and related_model_name in fields_map:
480
- related_fields_to_fetch.update(fields_map[related_model_name])
480
+ # Process each field, checking for custom serializers
481
+ from statezero.adaptors.django.serializers import get_custom_serializer
482
+ related_meta = _get_model_meta(related_model)
483
+ for field_name in fields_map[related_model_name]:
484
+ try:
485
+ field_obj = related_meta.get_field(field_name)
486
+ if not field_obj.is_relation:
487
+ # Check if this field has a custom serializer with explicit DB field requirements
488
+ custom_serializer = get_custom_serializer(field_obj.__class__)
489
+ if custom_serializer and hasattr(custom_serializer, 'get_prefetch_db_fields'):
490
+ # Use the explicit list from the custom serializer
491
+ db_fields = custom_serializer.get_prefetch_db_fields(field_name)
492
+ for db_field in db_fields:
493
+ related_fields_to_fetch.add(db_field)
494
+ logger.debug(f"Using custom DB fields {db_fields} for field '{field_name}' in {related_model_name}")
495
+ else:
496
+ # No custom serializer, just add the field itself
497
+ related_fields_to_fetch.add(field_name)
498
+ else:
499
+ # Relation field, add as-is
500
+ related_fields_to_fetch.add(field_name)
501
+ except FieldDoesNotExist:
502
+ # Field doesn't exist, add it anyway (might be computed)
503
+ related_fields_to_fetch.add(field_name)
504
+ except Exception as e:
505
+ logger.error(f"Error checking custom serializer for field '{field_name}' in {related_model_name}: {e}")
506
+ # On error, add the field anyway to be safe
507
+ related_fields_to_fetch.add(field_name)
481
508
  else:
482
509
  # If no field restrictions are provided, get all fields
483
510
  all_fields = [f.name for f in related_model._meta.get_fields() if f.concrete]
@@ -531,7 +558,18 @@ def optimize_query(queryset, fields=None, fields_map=None, depth=0, use_only=Tru
531
558
  try:
532
559
  field_obj = root_meta.get_field(field_name)
533
560
  if not field_obj.is_relation:
534
- root_fields_to_fetch.add(field_name)
561
+ # Check if this field has a custom serializer with explicit DB field requirements
562
+ from statezero.adaptors.django.serializers import get_custom_serializer
563
+ custom_serializer = get_custom_serializer(field_obj.__class__)
564
+ if custom_serializer and hasattr(custom_serializer, 'get_prefetch_db_fields'):
565
+ # Use the explicit list from the custom serializer
566
+ db_fields = custom_serializer.get_prefetch_db_fields(field_name)
567
+ for db_field in db_fields:
568
+ root_fields_to_fetch.add(db_field)
569
+ logger.debug(f"Using custom DB fields {db_fields} for field '{field_name}'")
570
+ else:
571
+ # No custom serializer, just add the field itself
572
+ root_fields_to_fetch.add(field_name)
535
573
  elif isinstance(field_obj, (ForeignKey, OneToOneField)):
536
574
  # If FK/O2O itself is requested directly, include its id field
537
575
  root_fields_to_fetch.add(field_obj.attname)
@@ -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.fields != "__all__":
44
+ if model_config.frontend_fields != "__all__":
45
45
  all_fields = [
46
- field for field in all_fields if field.name in model_config.fields
46
+ field for field in all_fields if field.name in model_config.frontend_fields
47
47
  ]
48
48
 
49
49
  for field in all_fields:
@@ -386,8 +386,19 @@ class ASTParser:
386
386
 
387
387
  # If any permission allows all fields
388
388
  if fields == "__all__":
389
- return all_fields
390
-
389
+ # NEW: For read operations, default "__all__" to frontend_fields
390
+ if operation_type == "read":
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
391
402
  # Add allowed fields from this permission
392
403
  else: # Ensure we're not operating on the string "__all__"
393
404
  fields &= all_fields # Ensure fields actually exist
@@ -181,6 +181,7 @@ 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,
184
185
  display: Optional[Any] = None,
185
186
  DEBUG: bool = False,
186
187
  ):
@@ -195,6 +196,7 @@ class ModelConfig:
195
196
  self.searchable_fields = searchable_fields or set()
196
197
  self.ordering_fields = ordering_fields or set()
197
198
  self.fields = fields or "__all__"
199
+ self.frontend_fields = frontend_fields or self.fields
198
200
  self.display = display
199
201
  self.DEBUG = DEBUG or False
200
202
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: statezero
3
- Version: 0.1.0b12
3
+ Version: 0.1.0b14
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