django-ninja-aio-crud 2.18.1__tar.gz → 2.18.2__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 (118) hide show
  1. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/CHANGELOG.md +89 -8
  2. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/PKG-INFO +1 -1
  3. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/__init__.py +1 -1
  4. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/models/utils.py +3 -80
  5. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/models/test_models_extra.py +0 -22
  6. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/.github/dependabot.yml +0 -0
  7. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/.github/workflows/coverage.yml +0 -0
  8. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/.github/workflows/docs.yml +0 -0
  9. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/.github/workflows/performance.yml +0 -0
  10. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/.github/workflows/publish.yml +0 -0
  11. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/.gitignore +0 -0
  12. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/.pre-commit-config.yaml +0 -0
  13. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/CLAUDE.md +0 -0
  14. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/LICENSE +0 -0
  15. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/README.md +0 -0
  16. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/CNAME +0 -0
  17. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/api/authentication.md +0 -0
  18. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/api/models/model_serializer.md +0 -0
  19. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/api/models/model_util.md +0 -0
  20. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/api/models/serializers.md +0 -0
  21. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/api/models/validators.md +0 -0
  22. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/api/pagination.md +0 -0
  23. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/api/renderers/orjson_renderer.md +0 -0
  24. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/api/views/api_view.md +0 -0
  25. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/api/views/api_view_set.md +0 -0
  26. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/api/views/decorators.md +0 -0
  27. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/api/views/mixins.md +0 -0
  28. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/auth.md +0 -0
  29. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/contributing.md +0 -0
  30. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/extra.css +0 -0
  31. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/getting_started/images/index/foo-index-create-swagger.png +0 -0
  32. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/getting_started/images/index/foo-index-delete-swagger.png +0 -0
  33. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/getting_started/images/index/foo-index-list-swagger.png +0 -0
  34. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/getting_started/images/index/foo-index-retrieve-swagger.png +0 -0
  35. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/getting_started/images/index/foo-index-swagger.png +0 -0
  36. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/getting_started/images/index/foo-index-update-swagger.png +0 -0
  37. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/getting_started/installation.md +0 -0
  38. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/getting_started/quick_start.md +0 -0
  39. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/getting_started/quick_start_serializer.md +0 -0
  40. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/images/bar-swagger.png +0 -0
  41. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/images/favicon.ico +0 -0
  42. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/images/foo-swagger.png +0 -0
  43. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/images/logo-full.png +0 -0
  44. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/images/logo.png +0 -0
  45. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/images/model_util/foo-reverse-relations-swagger.png +0 -0
  46. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/index.md +0 -0
  47. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/performance.md +0 -0
  48. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/release_notes.md +0 -0
  49. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/requirements.txt +0 -0
  50. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/tutorial/authentication.md +0 -0
  51. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/tutorial/crud.md +0 -0
  52. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/tutorial/filtering.md +0 -0
  53. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/tutorial/model.md +0 -0
  54. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/docs/tutorial/serializer.md +0 -0
  55. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/main.py +0 -0
  56. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/mkdocs.yml +0 -0
  57. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/api.py +0 -0
  58. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/auth.py +0 -0
  59. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/decorators/__init__.py +0 -0
  60. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/decorators/operations.py +0 -0
  61. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/decorators/views.py +0 -0
  62. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/exceptions.py +0 -0
  63. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/factory/__init__.py +0 -0
  64. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/factory/operations.py +0 -0
  65. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/helpers/__init__.py +0 -0
  66. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/helpers/api.py +0 -0
  67. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/helpers/query.py +0 -0
  68. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/models/__init__.py +0 -0
  69. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/models/serializers.py +0 -0
  70. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/parsers.py +0 -0
  71. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/renders.py +0 -0
  72. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/schemas/__init__.py +0 -0
  73. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/schemas/api.py +0 -0
  74. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/schemas/filters.py +0 -0
  75. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/schemas/generics.py +0 -0
  76. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/schemas/helpers.py +0 -0
  77. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/types.py +0 -0
  78. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/views/__init__.py +0 -0
  79. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/views/api.py +0 -0
  80. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/ninja_aio/views/mixins.py +0 -0
  81. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/overrides/main.html +0 -0
  82. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/overrides/partials/announce.html +0 -0
  83. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/pyproject.toml +0 -0
  84. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/requirements.dev.txt +0 -0
  85. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/run-local-coverage.sh +0 -0
  86. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/run-performance.sh +0 -0
  87. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/__init__.py +0 -0
  88. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/core/__init__.py +0 -0
  89. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/core/test_decorators.py +0 -0
  90. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/core/test_exceptions_api.py +0 -0
  91. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/core/test_renderer_parser.py +0 -0
  92. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/generics/__init__.py +0 -0
  93. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/generics/literals.py +0 -0
  94. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/generics/models.py +0 -0
  95. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/generics/request.py +0 -0
  96. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/generics/views.py +0 -0
  97. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/helpers/__init__.py +0 -0
  98. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/helpers/test_many_to_many_api.py +0 -0
  99. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/models/__init__.py +0 -0
  100. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/models/test_model_util.py +0 -0
  101. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/performance/__init__.py +0 -0
  102. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/performance/check_regression.py +0 -0
  103. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/performance/generate_report.py +0 -0
  104. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/performance/test_performance.py +0 -0
  105. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/test_app/__init__.py +0 -0
  106. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/test_app/models.py +0 -0
  107. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/test_app/schema.py +0 -0
  108. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/test_app/serializers.py +0 -0
  109. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/test_app/views.py +0 -0
  110. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/test_auth.py +0 -0
  111. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/test_decorators.py +0 -0
  112. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/test_exceptions.py +0 -0
  113. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/test_query_util.py +0 -0
  114. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/test_serializers.py +0 -0
  115. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/test_settings.py +0 -0
  116. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/views/__init__.py +0 -0
  117. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/views/test_views.py +0 -0
  118. {django_ninja_aio_crud-2.18.1 → django_ninja_aio_crud-2.18.2}/tests/views/test_viewset.py +0 -0
@@ -1,5 +1,94 @@
1
1
  # 📋 Release Notes
2
2
 
3
+ ## 🏷️ [v2.18.2] - 2026-02-02
4
+
5
+ ---
6
+
7
+ ### 🔧 Improvements
8
+
9
+ #### ✨ Removed Redundant Input Validation
10
+ > `ninja_aio/models/utils.py`
11
+
12
+ Removed redundant input field validation logic since Pydantic already validates all inputs before they reach the payload processing stage. This simplifies the codebase and properly handles field aliases and custom fields.
13
+
14
+ **Removed methods:**
15
+
16
+ | Method | Previous Line | Why Removed |
17
+ |---|---|---|
18
+ | `_validate_input_fields()` | 746-782 | Redundant - Pydantic validates all inputs during schema deserialization |
19
+ | `get_valid_input_fields()` | 198-237 | Only used by removed `_validate_input_fields()` method |
20
+
21
+ **Updated method:**
22
+ - `parse_input_data()` - Removed call to `_validate_input_fields()` and added clarifying comment that Pydantic handles all validation
23
+
24
+ **Why this improves the code:**
25
+
26
+ Since Django Ninja uses Pydantic to validate all inputs against generated schemas:
27
+ - ✅ Custom fields (defined via `custom_fields` parameter) are validated by Pydantic
28
+ - ✅ Field aliases are properly handled by Pydantic during deserialization
29
+ - ✅ By the time `parse_input_data()` receives the `Schema` instance, all validation has already occurred
30
+ - ✅ `model_dump()` simply converts the validated instance to a dict with proper field names
31
+
32
+ The removed validation was:
33
+ - ❌ Redundant (Pydantic already validated)
34
+ - ❌ Incomplete (couldn't properly handle all Pydantic features like aliases)
35
+ - ❌ Assuming custom fields and aliases couldn't be used in requests
36
+
37
+ **Example of what now works correctly:**
38
+
39
+ ```python
40
+ from pydantic import Field
41
+ from ninja_aio.models import Serializer, serializers
42
+
43
+ class UserSerializer(Serializer):
44
+ class Meta:
45
+ model = User
46
+ schema_in = serializers.SchemaModelConfig(
47
+ fields=["username", "email"],
48
+ custom_fields=[
49
+ ("display_name", str, Field(alias="displayName")) # Alias support
50
+ ]
51
+ )
52
+
53
+ # Input with alias now works properly:
54
+ # {"username": "john", "email": "john@example.com", "displayName": "John Doe"}
55
+ # Pydantic handles the alias → Validation passes → No redundant checks
56
+ ```
57
+
58
+ ---
59
+
60
+ ### 🧪 Tests
61
+
62
+ #### `ModelUtilHelperMethodsTestCase` — Removed 3 tests
63
+
64
+ **Removed tests:**
65
+
66
+ | Test | Reason |
67
+ |---|---|
68
+ | `test_validate_input_fields_valid_fields` | Method `_validate_input_fields` no longer exists |
69
+ | `test_validate_input_fields_invalid_fields` | Method `_validate_input_fields` no longer exists |
70
+ | `test_validate_input_fields_skips_custom_fields` | Method `_validate_input_fields` no longer exists |
71
+
72
+ **Test results:**
73
+ - ✅ 608 tests pass (down from 611)
74
+ - ✅ 100% coverage maintained on `ninja_aio/models/utils.py`
75
+ - ✅ 99% overall coverage maintained
76
+
77
+ ---
78
+
79
+ ### 🎯 Summary
80
+
81
+ **Django Ninja Aio CRUD v2.18.2** is a code quality improvement release that removes redundant validation logic. By trusting Pydantic's built-in validation, the codebase is simplified while properly supporting all Pydantic features including field aliases and custom fields. This change has no impact on end users since Pydantic was already handling validation - we simply removed the redundant secondary validation that was incomplete and caused issues with aliases.
82
+
83
+ **Key benefits:**
84
+ - 🧹 **Simpler Code** — Removed 70+ lines of redundant validation logic
85
+ - ✅ **Proper Alias Support** — Field aliases now work correctly without workarounds
86
+ - 🎯 **Trust the Framework** — Pydantic handles all input validation; no redundant checks needed
87
+ - 🔒 **Same Security** — No security impact since Pydantic validation was already the primary defense
88
+ - 🧪 **100% Coverage** — Maintained complete test coverage across the codebase
89
+
90
+ ---
91
+
3
92
  ## 🏷️ [v2.18.1] - 2026-02-01
4
93
 
5
94
  ---
@@ -365,14 +454,6 @@ Added comprehensive tests for all new functionality:
365
454
 
366
455
  Added comprehensive performance benchmarking infrastructure for monitoring framework performance during development.
367
456
 
368
- **New files:**
369
- - `tests/performance/__init__.py` - Performance test package
370
- - `tests/performance/test_performance.py` - 482 lines, 19 benchmarks
371
- - `tests/performance/generate_report.py` - 511 lines, interactive HTML report generator
372
- - `tests/performance/check_regression.py` - 172 lines, regression detection
373
- - `run-performance.sh` - Shell script to run benchmarks
374
- - `.github/workflows/performance.yml` - CI workflow for automated benchmarking
375
-
376
457
  **Benchmark categories:**
377
458
  - Schema generation (4 tests)
378
459
  - Serialization (4 tests)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-ninja-aio-crud
3
- Version: 2.18.1
3
+ Version: 2.18.2
4
4
  Summary: Django Ninja AIO CRUD - Rest Framework
5
5
  Author: Giuseppe Casillo
6
6
  Requires-Python: >=3.10, <3.15
@@ -1,6 +1,6 @@
1
1
  """Django Ninja AIO CRUD - Rest Framework"""
2
2
 
3
- __version__ = "2.18.1"
3
+ __version__ = "2.18.2"
4
4
 
5
5
  from .api import NinjaAIO
6
6
 
@@ -195,46 +195,6 @@ class ModelUtil:
195
195
  """
196
196
  return [field.name for field in self.model._meta.get_fields()]
197
197
 
198
- def get_valid_input_fields(
199
- self, is_serializer: bool, serializer: "ModelSerializer | None" = None
200
- ) -> set[str]:
201
- """
202
- Get allowlist of valid field names for input validation.
203
-
204
- Security: Prevents field injection by returning only fields that should
205
- be accepted from user input.
206
-
207
- Parameters
208
- ----------
209
- is_serializer : bool
210
- Whether using a ModelSerializer
211
- serializer : ModelSerializer, optional
212
- Serializer instance if applicable
213
-
214
- Returns
215
- -------
216
- set[str]
217
- Set of valid field names that can be accepted in input payloads
218
- """
219
- valid_fields = set(self.model_fields)
220
-
221
- # If using a serializer, also include custom fields
222
- if is_serializer and serializer:
223
- # Get all custom fields defined in the serializer
224
- try:
225
- # Custom fields are those that are not model fields but are defined
226
- # in the serializer configuration
227
- for schema_type in ['create', 'update', 'read', 'detail']:
228
- try:
229
- schema_fields = serializer.get_fields(schema_type)
230
- if schema_fields:
231
- valid_fields.update(schema_fields)
232
- except (AttributeError, TypeError):
233
- continue
234
- except (AttributeError, TypeError):
235
- pass
236
-
237
- return valid_fields
238
198
 
239
199
  @property
240
200
  def model_name(self) -> str:
@@ -743,43 +703,6 @@ class ModelUtil:
743
703
  obj = await self.get_object(request, query_data=query_data, is_for=is_for)
744
704
  return await self._bump_object_from_schema(obj, obj_schema)
745
705
 
746
- def _validate_input_fields(
747
- self, payload: dict, is_serializer: bool, serializer
748
- ) -> None:
749
- """
750
- Validate non-custom payload keys against model fields.
751
-
752
- Parameters
753
- ----------
754
- payload : dict
755
- Input payload to validate.
756
- is_serializer : bool
757
- Whether using a ModelSerializer.
758
- serializer : ModelSerializer | Serializer
759
- Serializer instance if applicable.
760
-
761
- Raises
762
- ------
763
- SerializeError
764
- If invalid field names are found in payload.
765
- """
766
- invalid_fields = []
767
- for key in payload.keys():
768
- # Skip custom fields - they're validated by Pydantic schema
769
- if is_serializer and serializer.is_custom(key):
770
- continue
771
- # Validate non-custom fields exist on the model
772
- if key not in self.model_fields:
773
- invalid_fields.append(key)
774
-
775
- if invalid_fields:
776
- raise SerializeError(
777
- {
778
- "detail": f"Invalid field names in payload: {', '.join(sorted(invalid_fields))}",
779
- "invalid_fields": sorted(invalid_fields),
780
- },
781
- 400,
782
- )
783
706
 
784
707
  def _collect_custom_and_optional_fields(
785
708
  self, payload: dict, is_serializer: bool, serializer
@@ -888,7 +811,7 @@ class ModelUtil:
888
811
 
889
812
  Steps
890
813
  -----
891
- - Validate fields against allowlist (security).
814
+ - Validate fields against schema (including aliases and custom fields).
892
815
  - Strip custom fields (retain separately).
893
816
  - Drop optional fields with None (ModelSerializer only).
894
817
  - Decode BinaryField base64 values.
@@ -917,8 +840,8 @@ class ModelUtil:
917
840
  )
918
841
  serializer = self.serializer if self.with_serializer else self.model
919
842
 
920
- # Security: Validate non-custom payload keys against model fields
921
- self._validate_input_fields(payload, is_serializer, serializer)
843
+ # Note: Field validation is handled by Pydantic during schema deserialization
844
+ # No additional validation needed here since data is already a validated Schema instance
922
845
 
923
846
  # Collect custom and optional fields
924
847
  customs, optionals = self._collect_custom_and_optional_fields(
@@ -537,28 +537,6 @@ class ModelUtilHelperMethodsTestCase(TestCase):
537
537
  cls.util_custom = ModelUtil(CustomOptionalSerializer)
538
538
  cls.util_plain = ModelUtil(app_models.TestModelSerializer)
539
539
 
540
- def test_validate_input_fields_valid_fields(self):
541
- """_validate_input_fields should not raise for valid fields."""
542
- payload = {"name": "test", "description": "desc"}
543
- # Should not raise
544
- self.util_plain._validate_input_fields(payload, False, None)
545
-
546
- def test_validate_input_fields_invalid_fields(self):
547
- """_validate_input_fields should raise SerializeError for invalid fields."""
548
- payload = {"name": "test", "invalid_field": "value"}
549
- with self.assertRaises(SerializeError) as cm:
550
- self.util_plain._validate_input_fields(payload, False, None)
551
- error_data = cm.exception.error
552
- self.assertIn("invalid_fields", error_data)
553
- self.assertIn("invalid_field", error_data["invalid_fields"])
554
-
555
- def test_validate_input_fields_skips_custom_fields(self):
556
- """_validate_input_fields should skip custom fields when using serializer."""
557
- payload = {"name": "test", "extra": "custom"}
558
- # Should not raise because 'extra' is a custom field
559
- self.util_custom._validate_input_fields(
560
- payload, True, CustomOptionalSerializer
561
- )
562
540
 
563
541
  def test_collect_custom_and_optional_fields_with_serializer(self):
564
542
  """_collect_custom_and_optional_fields should collect custom and optional fields."""