django-ninja-aio-crud 2.16.1__py3-none-any.whl → 2.17.0__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.
- {django_ninja_aio_crud-2.16.1.dist-info → django_ninja_aio_crud-2.17.0.dist-info}/METADATA +1 -1
- {django_ninja_aio_crud-2.16.1.dist-info → django_ninja_aio_crud-2.17.0.dist-info}/RECORD +6 -6
- ninja_aio/__init__.py +1 -1
- ninja_aio/models/serializers.py +96 -43
- {django_ninja_aio_crud-2.16.1.dist-info → django_ninja_aio_crud-2.17.0.dist-info}/WHEEL +0 -0
- {django_ninja_aio_crud-2.16.1.dist-info → django_ninja_aio_crud-2.17.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
ninja_aio/__init__.py,sha256=
|
|
1
|
+
ninja_aio/__init__.py,sha256=2QH5WmYL3ouCf-JfuHrYIk6jk3nDnu1ClpC1bPe5XWw,120
|
|
2
2
|
ninja_aio/api.py,sha256=tuC7vdvn7s1GkCnSFy9Kn1zv0glZfYptRQVvo8ZRtGQ,2429
|
|
3
3
|
ninja_aio/auth.py,sha256=f4yk45fLi36Qctu0A0zgHTFedb9yk3ewq5rOMpoPYIE,9035
|
|
4
4
|
ninja_aio/exceptions.py,sha256=w8QWmVlg88iJvBrNODSDBHSsy8nNpwngaCGWRnkXoPo,3899
|
|
@@ -14,7 +14,7 @@ ninja_aio/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
|
|
|
14
14
|
ninja_aio/helpers/api.py,sha256=va_HvZVBFm1KxwQhH4u09U4F1JS5JrQuRpRmPTHJt7w,21326
|
|
15
15
|
ninja_aio/helpers/query.py,sha256=Lqv4nrWYr543tC5K-SEcBottLID8cb83aDc26i2Wxj4,5053
|
|
16
16
|
ninja_aio/models/__init__.py,sha256=L3UQnQAlKoI3F7jinadL-Nn55hkPvnSRPYW0JtnbWFo,114
|
|
17
|
-
ninja_aio/models/serializers.py,sha256=
|
|
17
|
+
ninja_aio/models/serializers.py,sha256=IJvBdn4i3nXjD5GSKvEN3SXBrcOAZCVzQ-yMkWft9II,45342
|
|
18
18
|
ninja_aio/models/utils.py,sha256=lAXtc3YY7_n4f0jIacX4DSXhUOzMy7y5MsBnInNxtfk,32874
|
|
19
19
|
ninja_aio/schemas/__init__.py,sha256=dHILiYBKMb51lDcyQdiXRw_0nzqM7Lu81UX2hv7kEfo,837
|
|
20
20
|
ninja_aio/schemas/api.py,sha256=dGUpJXR1iAf93QNR4kYj1uqIkTjiMfXultCotY6GtaQ,361
|
|
@@ -24,7 +24,7 @@ ninja_aio/schemas/helpers.py,sha256=CpubwNXsZHtu8jddliyQybF1epwZ-GO60vHIuF5AR1Y,
|
|
|
24
24
|
ninja_aio/views/__init__.py,sha256=DEzjWA6y3WF0V10nNF8eEurLNEodgxKzyFd09AqVp3s,148
|
|
25
25
|
ninja_aio/views/api.py,sha256=AAqkj0xT8J3PmJvsbluZ33cfrmrXJHiV9ARe2BqnfQ8,22492
|
|
26
26
|
ninja_aio/views/mixins.py,sha256=Zl6J8gbVagwT85bzDuKyJTk3iFxxFgX0YgYkjiUxZGg,17040
|
|
27
|
-
django_ninja_aio_crud-2.
|
|
28
|
-
django_ninja_aio_crud-2.
|
|
29
|
-
django_ninja_aio_crud-2.
|
|
30
|
-
django_ninja_aio_crud-2.
|
|
27
|
+
django_ninja_aio_crud-2.17.0.dist-info/licenses/LICENSE,sha256=yrDAYcm0gRp_Qyzo3GQa4BjYjWRkAhGC8QRva__RYq0,1073
|
|
28
|
+
django_ninja_aio_crud-2.17.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
29
|
+
django_ninja_aio_crud-2.17.0.dist-info/METADATA,sha256=rU_PLwW8kGegOPLJmCnC07HdNT111NqrqsxJZIv8Rj4,9964
|
|
30
|
+
django_ninja_aio_crud-2.17.0.dist-info/RECORD,,
|
ninja_aio/__init__.py
CHANGED
ninja_aio/models/serializers.py
CHANGED
|
@@ -305,7 +305,9 @@ class BaseSerializer:
|
|
|
305
305
|
"""
|
|
306
306
|
# Auto-resolve ModelSerializer with readable fields
|
|
307
307
|
if isinstance(rel_model, ModelSerializerMeta):
|
|
308
|
-
has_readable_fields = rel_model.get_fields(
|
|
308
|
+
has_readable_fields = rel_model.get_fields(
|
|
309
|
+
"read"
|
|
310
|
+
) or rel_model.get_custom_fields("read")
|
|
309
311
|
return rel_model.generate_related_s() if has_readable_fields else None
|
|
310
312
|
|
|
311
313
|
# Resolve from explicit serializer mapping
|
|
@@ -341,7 +343,7 @@ class BaseSerializer:
|
|
|
341
343
|
- (name, py_type) -> default Ellipsis (required)
|
|
342
344
|
"""
|
|
343
345
|
raw_customs = cls._get_fields(s_type, "customs") or []
|
|
344
|
-
normalized: list[tuple[str,
|
|
346
|
+
normalized: list[tuple[str, Any, Any]] = []
|
|
345
347
|
for spec in raw_customs:
|
|
346
348
|
if not isinstance(spec, tuple):
|
|
347
349
|
raise ValueError(f"Custom field spec must be a tuple, got {type(spec)}")
|
|
@@ -373,8 +375,39 @@ class BaseSerializer:
|
|
|
373
375
|
|
|
374
376
|
@classmethod
|
|
375
377
|
def get_fields(cls, s_type: S_TYPES):
|
|
376
|
-
"""Return explicit declared
|
|
377
|
-
|
|
378
|
+
"""Return explicit declared field names for the serializer type (excludes inline customs)."""
|
|
379
|
+
fields = cls._get_fields(s_type, "fields")
|
|
380
|
+
# Filter out inline custom field tuples, return only string field names
|
|
381
|
+
return [f for f in fields if isinstance(f, str)]
|
|
382
|
+
|
|
383
|
+
@classmethod
|
|
384
|
+
def get_inline_customs(cls, s_type: S_TYPES) -> list[tuple[str, Any, Any]]:
|
|
385
|
+
"""
|
|
386
|
+
Return inline custom field tuples declared directly in the fields list.
|
|
387
|
+
|
|
388
|
+
These are tuples in the format (name, type, default) or (name, type) mixed
|
|
389
|
+
with regular string field names in the fields list.
|
|
390
|
+
|
|
391
|
+
Returns
|
|
392
|
+
-------
|
|
393
|
+
list[tuple[str, Any, Any]]
|
|
394
|
+
Normalized list of (name, type, default) tuples.
|
|
395
|
+
"""
|
|
396
|
+
fields = cls._get_fields(s_type, "fields")
|
|
397
|
+
inline_customs: list[tuple[str, Any, Any]] = []
|
|
398
|
+
for spec in fields:
|
|
399
|
+
if isinstance(spec, tuple):
|
|
400
|
+
match len(spec):
|
|
401
|
+
case 3:
|
|
402
|
+
inline_customs.append(spec)
|
|
403
|
+
case 2:
|
|
404
|
+
name, py_type = spec
|
|
405
|
+
inline_customs.append((name, py_type, ...))
|
|
406
|
+
case _:
|
|
407
|
+
raise ValueError(
|
|
408
|
+
f"Inline custom field tuple must have length 2 or 3 (name, type[, default]); got {len(spec)}"
|
|
409
|
+
)
|
|
410
|
+
return inline_customs
|
|
378
411
|
|
|
379
412
|
@classmethod
|
|
380
413
|
def is_custom(cls, field: str) -> bool:
|
|
@@ -430,10 +463,15 @@ class BaseSerializer:
|
|
|
430
463
|
# Handle relations_as_id for reverse relations
|
|
431
464
|
if field_name in relations_as_id:
|
|
432
465
|
from ninja_aio.models.utils import ModelUtil
|
|
466
|
+
|
|
433
467
|
pk_field_type = ModelUtil(rel_model).pk_field_type
|
|
434
468
|
if many:
|
|
435
469
|
# For many relations, use PkFromModel to extract PKs from model instances
|
|
436
|
-
return (
|
|
470
|
+
return (
|
|
471
|
+
field_name,
|
|
472
|
+
list[PkFromModel[pk_field_type]],
|
|
473
|
+
Field(default_factory=list),
|
|
474
|
+
)
|
|
437
475
|
else:
|
|
438
476
|
# For single reverse relations (ReverseOneToOne), extract pk
|
|
439
477
|
return (field_name, PkFromModel[pk_field_type] | None, None)
|
|
@@ -472,6 +510,7 @@ class BaseSerializer:
|
|
|
472
510
|
# Handle relations_as_id: serialize as the raw FK ID
|
|
473
511
|
if field_name in relations_as_id:
|
|
474
512
|
from ninja_aio.models.utils import ModelUtil
|
|
513
|
+
|
|
475
514
|
pk_field_type = ModelUtil(rel_model).pk_field_type
|
|
476
515
|
# Use PkFromModel to extract pk from the related instance during serialization
|
|
477
516
|
return (field_name, PkFromModel[pk_field_type] | None, None)
|
|
@@ -614,11 +653,18 @@ class BaseSerializer:
|
|
|
614
653
|
if forward:
|
|
615
654
|
forward_rels.append(forward)
|
|
616
655
|
|
|
656
|
+
# Combine explicit customs, inline customs, and forward relation schemas
|
|
657
|
+
all_customs = (
|
|
658
|
+
cls.get_custom_fields(fields_type)
|
|
659
|
+
+ cls.get_inline_customs(fields_type)
|
|
660
|
+
+ forward_rels
|
|
661
|
+
)
|
|
662
|
+
|
|
617
663
|
return (
|
|
618
664
|
fields,
|
|
619
665
|
reverse_rels,
|
|
620
666
|
cls.get_excluded_fields(fields_type),
|
|
621
|
-
|
|
667
|
+
all_customs,
|
|
622
668
|
cls.get_optional_fields(fields_type),
|
|
623
669
|
)
|
|
624
670
|
|
|
@@ -666,7 +712,11 @@ class BaseSerializer:
|
|
|
666
712
|
s_type = "create" if schema_type == "In" else "update"
|
|
667
713
|
fields = cls.get_fields(s_type)
|
|
668
714
|
optionals = cls.get_optional_fields(s_type)
|
|
669
|
-
customs =
|
|
715
|
+
customs = (
|
|
716
|
+
cls.get_custom_fields(s_type)
|
|
717
|
+
+ optionals
|
|
718
|
+
+ cls.get_inline_customs(s_type)
|
|
719
|
+
)
|
|
670
720
|
excludes = cls.get_excluded_fields(s_type)
|
|
671
721
|
|
|
672
722
|
# If no explicit fields and no excludes specified
|
|
@@ -698,17 +748,19 @@ class BaseSerializer:
|
|
|
698
748
|
def get_related_schema_data(cls):
|
|
699
749
|
"""
|
|
700
750
|
Build field/custom lists for 'Related' schema, flattening non-relational fields.
|
|
751
|
+
|
|
752
|
+
Custom fields (both explicit and inline) are always included since they
|
|
753
|
+
are computed/synthetic and not relation descriptors.
|
|
701
754
|
"""
|
|
702
755
|
fields = cls.get_fields("read")
|
|
703
|
-
|
|
704
|
-
name: (value, default)
|
|
705
|
-
for name, value, default in cls.get_custom_fields("read")
|
|
706
|
-
}
|
|
707
|
-
_related_fields = []
|
|
756
|
+
customs = cls.get_custom_fields("read") + cls.get_inline_customs("read")
|
|
708
757
|
model = cls._get_model()
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
758
|
+
|
|
759
|
+
# Filter out relation fields from model fields
|
|
760
|
+
non_relation_fields = []
|
|
761
|
+
for f in fields:
|
|
762
|
+
field_obj = getattr(model, f, None)
|
|
763
|
+
if field_obj is None or not isinstance(
|
|
712
764
|
field_obj,
|
|
713
765
|
(
|
|
714
766
|
ManyToManyDescriptor,
|
|
@@ -718,14 +770,13 @@ class BaseSerializer:
|
|
|
718
770
|
ForwardOneToOneDescriptor,
|
|
719
771
|
),
|
|
720
772
|
):
|
|
721
|
-
|
|
722
|
-
|
|
773
|
+
non_relation_fields.append(f)
|
|
774
|
+
|
|
775
|
+
# No fields or customs means nothing to include
|
|
776
|
+
if not non_relation_fields and not customs:
|
|
723
777
|
return None, None
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
]
|
|
727
|
-
related_fields = [f for f in _related_fields if f not in custom_f]
|
|
728
|
-
return related_fields, custom_related_fields
|
|
778
|
+
|
|
779
|
+
return non_relation_fields, customs
|
|
729
780
|
|
|
730
781
|
@classmethod
|
|
731
782
|
def generate_read_s(cls, depth: int = 1) -> Schema:
|
|
@@ -810,9 +861,9 @@ class ModelSerializer(models.Model, BaseSerializer, metaclass=ModelSerializerMet
|
|
|
810
861
|
Disallowed model fields on create (e.g., id, timestamps).
|
|
811
862
|
"""
|
|
812
863
|
|
|
813
|
-
fields: list[str] = []
|
|
814
|
-
customs: list[tuple[str,
|
|
815
|
-
optionals: list[tuple[str,
|
|
864
|
+
fields: list[str | tuple[str, Any, Any]] = []
|
|
865
|
+
customs: list[tuple[str, Any, Any]] = []
|
|
866
|
+
optionals: list[tuple[str, Any]] = []
|
|
816
867
|
excludes: list[str] = []
|
|
817
868
|
|
|
818
869
|
class ReadSerializer:
|
|
@@ -832,9 +883,9 @@ class ModelSerializer(models.Model, BaseSerializer, metaclass=ModelSerializerMet
|
|
|
832
883
|
Relation fields to serialize as IDs instead of nested objects.
|
|
833
884
|
"""
|
|
834
885
|
|
|
835
|
-
fields: list[str] = []
|
|
836
|
-
customs: list[tuple[str,
|
|
837
|
-
optionals: list[tuple[str,
|
|
886
|
+
fields: list[str | tuple[str, Any, Any]] = []
|
|
887
|
+
customs: list[tuple[str, Any, Any]] = []
|
|
888
|
+
optionals: list[tuple[str, Any]] = []
|
|
838
889
|
excludes: list[str] = []
|
|
839
890
|
relations_as_id: list[str] = []
|
|
840
891
|
|
|
@@ -853,9 +904,9 @@ class ModelSerializer(models.Model, BaseSerializer, metaclass=ModelSerializerMet
|
|
|
853
904
|
Optional output fields.
|
|
854
905
|
"""
|
|
855
906
|
|
|
856
|
-
fields: list[str] = []
|
|
857
|
-
customs: list[tuple[str,
|
|
858
|
-
optionals: list[tuple[str,
|
|
907
|
+
fields: list[str | tuple[str, Any, Any]] = []
|
|
908
|
+
customs: list[tuple[str, Any, Any]] = []
|
|
909
|
+
optionals: list[tuple[str, Any]] = []
|
|
859
910
|
excludes: list[str] = []
|
|
860
911
|
|
|
861
912
|
class UpdateSerializer:
|
|
@@ -873,9 +924,9 @@ class ModelSerializer(models.Model, BaseSerializer, metaclass=ModelSerializerMet
|
|
|
873
924
|
Immutable / blocked fields.
|
|
874
925
|
"""
|
|
875
926
|
|
|
876
|
-
fields: list[str] = []
|
|
877
|
-
customs: list[tuple[str,
|
|
878
|
-
optionals: list[tuple[str,
|
|
927
|
+
fields: list[str | tuple[str, Any, Any]] = []
|
|
928
|
+
customs: list[tuple[str, Any, Any]] = []
|
|
929
|
+
optionals: list[tuple[str, Any]] = []
|
|
879
930
|
excludes: list[str] = []
|
|
880
931
|
|
|
881
932
|
# Serializer type to configuration class mapping
|
|
@@ -1044,20 +1095,22 @@ class SchemaModelConfig(Schema):
|
|
|
1044
1095
|
Configuration container for declarative schema definitions.
|
|
1045
1096
|
Attributes
|
|
1046
1097
|
----------
|
|
1047
|
-
fields : Optional[List[str]]
|
|
1048
|
-
Explicit model fields to include.
|
|
1049
|
-
|
|
1050
|
-
|
|
1098
|
+
fields : Optional[List[str | tuple]]
|
|
1099
|
+
Explicit model fields to include. Can also contain inline custom field tuples:
|
|
1100
|
+
- 2-tuple: (name, type) - required field
|
|
1101
|
+
- 3-tuple: (name, type, default) - optional field with default
|
|
1102
|
+
optionals : Optional[List[tuple[str, Any]]]
|
|
1103
|
+
Optional model fields. Type can be any valid type annotation including Union.
|
|
1051
1104
|
exclude : Optional[List[str]]
|
|
1052
1105
|
Model fields to exclude.
|
|
1053
|
-
customs : Optional[List[tuple[str,
|
|
1054
|
-
Custom / synthetic fields.
|
|
1106
|
+
customs : Optional[List[tuple[str, Any, Any]]]
|
|
1107
|
+
Custom / synthetic fields. Type can be any valid type annotation including Union.
|
|
1055
1108
|
"""
|
|
1056
1109
|
|
|
1057
|
-
fields: Optional[List[str]] = None
|
|
1058
|
-
optionals: Optional[List[tuple[str,
|
|
1110
|
+
fields: Optional[List[str | tuple[str, Any, Any] | tuple[str, Any]]] = None
|
|
1111
|
+
optionals: Optional[List[tuple[str, Any]]] = None
|
|
1059
1112
|
exclude: Optional[List[str]] = None
|
|
1060
|
-
customs: Optional[List[tuple[str,
|
|
1113
|
+
customs: Optional[List[tuple[str, Any, Any]]] = None
|
|
1061
1114
|
|
|
1062
1115
|
|
|
1063
1116
|
class Serializer(BaseSerializer, metaclass=SerializerMeta):
|
|
File without changes
|
{django_ninja_aio_crud-2.16.1.dist-info → django_ninja_aio_crud-2.17.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|