statezero 0.1.0b12__py3-none-any.whl → 0.1.0b14__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.
@@ -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
statezero/core/config.py CHANGED
@@ -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
@@ -12,15 +12,15 @@ statezero/adaptors/django/helpers.py,sha256=0Dyq5vboDuTUaH-KpS3oVDjastA9yv6xI6Xp
12
12
  statezero/adaptors/django/middleware.py,sha256=YVr8fkqCk51xJQM-ovtrUiB9Kt9H81cLd9xv4cM9YlM,410
13
13
  statezero/adaptors/django/orm.py,sha256=Z62XheCvuKIpKOoIIiLLOwrpJ5jPhv-BGxg-pqPgNaU,40757
14
14
  statezero/adaptors/django/permissions.py,sha256=fU2c4bKK0zX2uuVB0UazZHTI-5OkiI5-BtPNcPEWmW0,9525
15
- statezero/adaptors/django/query_optimizer.py,sha256=-GNqL7Xn8WP8OsLEAAxXpIszSyEwm-l6WjgdkEFzxUM,38541
16
- statezero/adaptors/django/schemas.py,sha256=aIUbTvyEsQUWYN_g4oiorih_gOhT8ti-bLEK4TMk7Tw,13449
15
+ statezero/adaptors/django/query_optimizer.py,sha256=-iAh5kyE8WNZdjb_qBbNag_nxKzejroUYPBdwG_uVaQ,41462
16
+ statezero/adaptors/django/schemas.py,sha256=YYWvtg-SNcARBZPl51zb4tY2BStcXDYbprKSyS3mIbM,13467
17
17
  statezero/adaptors/django/serializers.py,sha256=YFFDu6bzoWkSEOVH5Wmc4yJ8SaOkUA6HbXXYt6djlfc,23296
18
18
  statezero/adaptors/django/urls.py,sha256=OrGQ60vj_wrbiREAKmYDZTwohpKmgjH9n0fdOw1qPaY,924
19
19
  statezero/adaptors/django/views.py,sha256=2bJDbXuRGoG2_7zyapWzmRzpSVUHkCpcI58wsrXN1jc,19947
20
20
  statezero/adaptors/django/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  statezero/adaptors/django/extensions/custom_field_serializers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  statezero/adaptors/django/extensions/custom_field_serializers/file_fields.py,sha256=BaOaJPmyzCp-YFwpsTOvGHjHpk6s8UJuZ5JsF-PEGV4,4518
23
- statezero/adaptors/django/extensions/custom_field_serializers/money_field.py,sha256=pDgwF_oZgOL02P0aJ4TIZJKMhtKD8ioaqfgrxRblQvQ,2767
23
+ statezero/adaptors/django/extensions/custom_field_serializers/money_field.py,sha256=Oms0_hOaxpeTDI7JhgwFud8kYIVxX-fEflO7C1QBi9I,3083
24
24
  statezero/adaptors/django/migrations/0001_initial.py,sha256=F5kr819sPN5_xc82GpX6-e5CwVWDvywePWC0cv9zYKY,1318
25
25
  statezero/adaptors/django/migrations/0002_delete_modelviewsubscription.py,sha256=7fJXl18gvMdEjKnQbO2h8mPAl4jNGVCAb60NRl5V6tI,317
26
26
  statezero/adaptors/django/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -29,10 +29,10 @@ statezero/adaptors/django/search_providers/basic_search.py,sha256=5_GJ1r_B6JdIYX
29
29
  statezero/adaptors/django/search_providers/postgres_search.py,sha256=IMoHxzfi-Y3hAxPND4Xc6GatrPs1eXAqpmcfwt5Zr14,2459
30
30
  statezero/core/__init__.py,sha256=Z6RTutAAElLMEjBFphVVmpySPdJBA55j-Uo0BtR7c5E,1040
31
31
  statezero/core/actions.py,sha256=eq4zuDhK1h-nZ24jUQhiWL6BcMqq-W4BhdVNdYegymw,2969
32
- statezero/core/ast_parser.py,sha256=QHpwWlVQTrxE1xen4xMZcALDn7eEl4CL5PoVfGQg_c4,38539
32
+ statezero/core/ast_parser.py,sha256=QTFRDwanN2jJJOs4SeRdRPiVZWlK-tkmwZBlQNByew0,39321
33
33
  statezero/core/ast_validator.py,sha256=YZAflPyba0kXWBNhd1Z_XeEk-_zUzM6MkY9zSlX1PMs,11582
34
34
  statezero/core/classes.py,sha256=NLIiOPm6PwQAmJXFeN2iScdoGeHyimzdnXmyyfBM-NY,6977
35
- statezero/core/config.py,sha256=RNRujZg393a2B_uj4isUUIhwEUZFAfSlafKHu_O3EAs,11679
35
+ statezero/core/config.py,sha256=kOcQPzBA06d8EliP2bVY0daFlt8bm-8ZOsqb5x7-8JA,11822
36
36
  statezero/core/context_storage.py,sha256=DVx525ZMRorj41kg5K0N6pPdGkQ5_XEJcBucpH5ChxQ,162
37
37
  statezero/core/event_bus.py,sha256=2IFLBHSkLzpm1AX0MfSXSmF2X-lXK-gOoODZCtB2Jdw,6284
38
38
  statezero/core/event_emitters.py,sha256=qjMbeUmdn4bG7WiVfqTmNdaflEea5amnTEpOn5X0J44,2046
@@ -41,8 +41,8 @@ statezero/core/hook_checks.py,sha256=uqtvwRx1qGsF7Vc49elAWdOjMzhuv3RADKY1wiLvhK4
41
41
  statezero/core/interfaces.py,sha256=kVkNWyh52tUlzD02CRheLJof3DyQoVcPuvX33fL6sn8,20544
42
42
  statezero/core/process_request.py,sha256=dwIeBEVOE8zA-oE1h65XNOGiVqFbbXA7SzTAguLNgZk,8060
43
43
  statezero/core/types.py,sha256=mMtqK3fGhEM6LtzUgQrxlyP-V0VgVqc-1eVKgRjTzp0,913
44
- statezero-0.1.0b12.dist-info/licenses/license.md,sha256=0uKjybTt9K_YbEmYgf25JN292qjjJ-BPofvIZ3wdtX4,7411
45
- statezero-0.1.0b12.dist-info/METADATA,sha256=Y05TVxWKkQXusJoSlwNNZopwomosIaahwk-B9ClNvag,6704
46
- statezero-0.1.0b12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
47
- statezero-0.1.0b12.dist-info/top_level.txt,sha256=UAuZYPKczradU1kcMQxsGjUzEW0qdgsqzhXyscrcLpw,10
48
- statezero-0.1.0b12.dist-info/RECORD,,
44
+ statezero-0.1.0b14.dist-info/licenses/license.md,sha256=0uKjybTt9K_YbEmYgf25JN292qjjJ-BPofvIZ3wdtX4,7411
45
+ statezero-0.1.0b14.dist-info/METADATA,sha256=uhyUyV7nb4_H58RrviWtMXDzZjQDTKxyvUSBxsl38Fo,6704
46
+ statezero-0.1.0b14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
47
+ statezero-0.1.0b14.dist-info/top_level.txt,sha256=UAuZYPKczradU1kcMQxsGjUzEW0qdgsqzhXyscrcLpw,10
48
+ statezero-0.1.0b14.dist-info/RECORD,,