django-ninja-aio-crud 2.15.0__tar.gz → 2.16.0__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.
Potentially problematic release.
This version of django-ninja-aio-crud might be problematic. Click here for more details.
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/PKG-INFO +1 -1
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/__init__.py +1 -1
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/auth.py +0 -5
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/helpers/api.py +11 -1
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/models/serializers.py +6 -8
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/schemas/helpers.py +8 -2
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/helpers/test_many_to_many_api.py +155 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/test_auth.py +2 -3
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/views/test_viewset.py +1 -1
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/.github/dependabot.yml +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/.github/workflows/coverage.yml +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/.github/workflows/docs.yml +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/.github/workflows/publish.yml +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/.gitignore +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/.pre-commit-config.yaml +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/LICENSE +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/README.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/CNAME +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/api/authentication.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/api/models/model_serializer.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/api/models/model_util.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/api/models/serializers.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/api/pagination.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/api/renderers/orjson_renderer.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/api/views/api_view.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/api/views/api_view_set.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/api/views/decorators.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/api/views/mixins.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/auth.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/contributing.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/extra.css +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/getting_started/images/index/foo-index-create-swagger.png +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/getting_started/images/index/foo-index-delete-swagger.png +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/getting_started/images/index/foo-index-list-swagger.png +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/getting_started/images/index/foo-index-retrieve-swagger.png +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/getting_started/images/index/foo-index-swagger.png +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/getting_started/images/index/foo-index-update-swagger.png +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/getting_started/installation.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/getting_started/quick_start.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/images/bar-swagger.png +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/images/favicon.ico +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/images/foo-swagger.png +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/images/logo.png +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/images/model_util/foo-reverse-relations-swagger.png +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/index.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/release_notes.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/requirements.txt +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/tutorial/authentication.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/tutorial/crud.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/tutorial/filtering.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/tutorial/model.md +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/main.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/mkdocs.yml +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/api.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/decorators/__init__.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/decorators/operations.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/decorators/views.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/exceptions.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/factory/__init__.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/factory/operations.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/helpers/__init__.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/helpers/query.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/models/__init__.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/models/utils.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/parsers.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/renders.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/schemas/__init__.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/schemas/api.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/schemas/filters.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/schemas/generics.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/types.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/views/__init__.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/views/api.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/views/mixins.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/pyproject.toml +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/requirements.dev.txt +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/run-local-coverage.sh +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/__init__.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/core/__init__.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/core/test_decorators.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/core/test_exceptions_api.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/core/test_renderer_parser.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/generics/__init__.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/generics/literals.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/generics/models.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/generics/request.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/generics/views.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/helpers/__init__.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/models/__init__.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/models/test_model_util.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/models/test_models_extra.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/test_app/__init__.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/test_app/models.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/test_app/schema.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/test_app/serializers.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/test_app/views.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/test_decorators.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/test_exceptions.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/test_query_util.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/test_serializers.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/test_settings.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/views/__init__.py +0 -0
- {django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/views/test_views.py +0 -0
|
@@ -107,11 +107,6 @@ class AsyncJwtBearer(HttpBearer):
|
|
|
107
107
|
"""
|
|
108
108
|
try:
|
|
109
109
|
self.dcd = jwt.decode(token, self.jwt_public, algorithms=self.algorithms)
|
|
110
|
-
except ValueError:
|
|
111
|
-
# raise AuthError(", ".join(exc.args), 401)
|
|
112
|
-
return False
|
|
113
|
-
|
|
114
|
-
try:
|
|
115
110
|
self.validate_claims(self.dcd.claims)
|
|
116
111
|
except errors.JoseError:
|
|
117
112
|
return False
|
|
@@ -359,6 +359,7 @@ class ManyToManyAPI:
|
|
|
359
359
|
filters_schema,
|
|
360
360
|
append_slash: bool,
|
|
361
361
|
verbose_name_plural: str,
|
|
362
|
+
decorators: list,
|
|
362
363
|
):
|
|
363
364
|
@self.router.get(
|
|
364
365
|
self._get_api_path(rel_path, append_slash=append_slash),
|
|
@@ -373,6 +374,7 @@ class ManyToManyAPI:
|
|
|
373
374
|
@decorate_view(
|
|
374
375
|
unique_view(f"get_{self.related_model_util.model_name}_{rel_path}"),
|
|
375
376
|
paginate(self.pagination_class),
|
|
377
|
+
*decorators,
|
|
376
378
|
)
|
|
377
379
|
async def get_related(
|
|
378
380
|
request: HttpRequest,
|
|
@@ -407,6 +409,7 @@ class ManyToManyAPI:
|
|
|
407
409
|
m2m_add: bool,
|
|
408
410
|
m2m_remove: bool,
|
|
409
411
|
verbose_name_plural: str,
|
|
412
|
+
decorators: list,
|
|
410
413
|
):
|
|
411
414
|
action, schema_in = self._resolve_action_schema(m2m_add, m2m_remove)
|
|
412
415
|
plural = verbose_name_plural
|
|
@@ -422,7 +425,10 @@ class ManyToManyAPI:
|
|
|
422
425
|
summary=summary,
|
|
423
426
|
description=summary,
|
|
424
427
|
)
|
|
425
|
-
@
|
|
428
|
+
@decorate_view(
|
|
429
|
+
unique_view(f"manage_{self.related_model_util.model_name}_{rel_path}"),
|
|
430
|
+
*decorators,
|
|
431
|
+
)
|
|
426
432
|
async def manage_related(
|
|
427
433
|
request: HttpRequest,
|
|
428
434
|
pk: Path[self.path_schema], # type: ignore
|
|
@@ -483,6 +489,8 @@ class ManyToManyAPI:
|
|
|
483
489
|
relation.verbose_name_plural
|
|
484
490
|
or rel_util.model._meta.verbose_name_plural.capitalize()
|
|
485
491
|
)
|
|
492
|
+
get_decorators = relation.get_decorators or []
|
|
493
|
+
post_decorators = relation.post_decorators or []
|
|
486
494
|
|
|
487
495
|
if m2m_get:
|
|
488
496
|
self._register_get_relation_view(
|
|
@@ -494,6 +502,7 @@ class ManyToManyAPI:
|
|
|
494
502
|
filters_schema=filters_schema,
|
|
495
503
|
append_slash=append_slash,
|
|
496
504
|
verbose_name_plural=verbose_name_plural,
|
|
505
|
+
decorators=get_decorators,
|
|
497
506
|
)
|
|
498
507
|
|
|
499
508
|
if m2m_add or m2m_remove:
|
|
@@ -505,6 +514,7 @@ class ManyToManyAPI:
|
|
|
505
514
|
m2m_add=m2m_add,
|
|
506
515
|
m2m_remove=m2m_remove,
|
|
507
516
|
verbose_name_plural=verbose_name_plural,
|
|
517
|
+
decorators=post_decorators,
|
|
508
518
|
)
|
|
509
519
|
|
|
510
520
|
def _add_views(self):
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/models/serializers.py
RENAMED
|
@@ -305,9 +305,8 @@ class BaseSerializer:
|
|
|
305
305
|
"""
|
|
306
306
|
# Auto-resolve ModelSerializer with readable fields
|
|
307
307
|
if isinstance(rel_model, ModelSerializerMeta):
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
return None
|
|
308
|
+
has_readable_fields = rel_model.get_fields("read") or rel_model.get_custom_fields("read")
|
|
309
|
+
return rel_model.generate_related_s() if has_readable_fields else None
|
|
311
310
|
|
|
312
311
|
# Resolve from explicit serializer mapping
|
|
313
312
|
rel_serializers = cls._get_relations_serializers() or {}
|
|
@@ -478,11 +477,10 @@ class BaseSerializer:
|
|
|
478
477
|
return (field_name, PkFromModel[pk_field_type] | None, None)
|
|
479
478
|
|
|
480
479
|
# Special case: ModelSerializer with no readable fields should be skipped entirely
|
|
481
|
-
if isinstance(rel_model, ModelSerializerMeta)
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
return None
|
|
480
|
+
if isinstance(rel_model, ModelSerializerMeta) and not (
|
|
481
|
+
rel_model.get_fields("read") or rel_model.get_custom_fields("read")
|
|
482
|
+
):
|
|
483
|
+
return None
|
|
486
484
|
|
|
487
485
|
schema = cls._resolve_relation_schema(field_name, rel_model)
|
|
488
486
|
if not schema:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import List, Optional, Type
|
|
1
|
+
from typing import Callable, List, Optional, Type
|
|
2
2
|
|
|
3
3
|
from ninja import Schema
|
|
4
4
|
from ninja_aio.types import ModelSerializerMeta, SerializerMeta
|
|
@@ -46,7 +46,11 @@ class M2MRelationSchema(BaseModel):
|
|
|
46
46
|
Whether to append a trailing slash to the generated GET endpoint path. Defaults to False for backward compatibility.
|
|
47
47
|
verbose_name_plural (str | None):
|
|
48
48
|
Optional human-readable plural name for the related model, used in documentation and responses.
|
|
49
|
-
|
|
49
|
+
get_decorators (List | None):
|
|
50
|
+
Optional list of decorators to apply to the GET endpoint.
|
|
51
|
+
post_decorators (List | None):
|
|
52
|
+
Optional list of decorators to apply to the POST (add/remove) endpoints.
|
|
53
|
+
|
|
50
54
|
Validation:
|
|
51
55
|
- If `model` is not a ModelSerializerMeta, `related_schema` is required.
|
|
52
56
|
- When `model` is a ModelSerializerMeta and `related_schema` is not provided, it will be
|
|
@@ -72,6 +76,8 @@ class M2MRelationSchema(BaseModel):
|
|
|
72
76
|
serializer_class: Optional[SerializerMeta] = None
|
|
73
77
|
append_slash: bool = False
|
|
74
78
|
verbose_name_plural: Optional[str] = None
|
|
79
|
+
get_decorators: Optional[List[Callable]] = []
|
|
80
|
+
post_decorators: Optional[List[Callable]] = []
|
|
75
81
|
|
|
76
82
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
77
83
|
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/helpers/test_many_to_many_api.py
RENAMED
|
@@ -280,3 +280,158 @@ class M2MRelationSchemaValidationTestCase(TestCase):
|
|
|
280
280
|
serializer_class=TestModelReverseManyToManySerializer,
|
|
281
281
|
)
|
|
282
282
|
self.assertEqual(schema.related_schema, CustomSchema)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
# Decorator tracking for tests
|
|
286
|
+
_decorator_calls = {"get": 0, "post": 0}
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def track_get_decorator(func):
|
|
290
|
+
"""Decorator that tracks GET endpoint calls."""
|
|
291
|
+
from functools import wraps
|
|
292
|
+
|
|
293
|
+
@wraps(func)
|
|
294
|
+
async def wrapper(*args, **kwargs):
|
|
295
|
+
_decorator_calls["get"] += 1
|
|
296
|
+
return await func(*args, **kwargs)
|
|
297
|
+
|
|
298
|
+
return wrapper
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def track_post_decorator(func):
|
|
302
|
+
"""Decorator that tracks POST endpoint calls."""
|
|
303
|
+
from functools import wraps
|
|
304
|
+
|
|
305
|
+
@wraps(func)
|
|
306
|
+
async def wrapper(*args, **kwargs):
|
|
307
|
+
_decorator_calls["post"] += 1
|
|
308
|
+
return await func(*args, **kwargs)
|
|
309
|
+
|
|
310
|
+
return wrapper
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
class TestM2MWithDecoratorsViewSet(GenericAPIViewSet):
|
|
314
|
+
"""ViewSet using M2M relations with custom decorators."""
|
|
315
|
+
|
|
316
|
+
model = models.TestModelSerializerManyToMany
|
|
317
|
+
m2m_relations = [
|
|
318
|
+
M2MRelationSchema(
|
|
319
|
+
model=models.TestModelSerializerReverseManyToMany,
|
|
320
|
+
related_name="test_model_serializers",
|
|
321
|
+
filters={"name": (str, "")},
|
|
322
|
+
get_decorators=[track_get_decorator],
|
|
323
|
+
post_decorators=[track_post_decorator],
|
|
324
|
+
append_slash=True,
|
|
325
|
+
)
|
|
326
|
+
]
|
|
327
|
+
|
|
328
|
+
def test_model_serializers_query_params_handler(self, queryset, filters):
|
|
329
|
+
name_filter = filters.get("name")
|
|
330
|
+
if name_filter:
|
|
331
|
+
queryset = queryset.filter(name=name_filter)
|
|
332
|
+
return queryset
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
@tag("many_to_many_api", "decorators")
|
|
336
|
+
class M2MRelationSchemaDecoratorsTestCase(Tests.BaseManyToManyAPITestCase):
|
|
337
|
+
"""Test M2M API with custom decorators applied to GET and POST endpoints."""
|
|
338
|
+
|
|
339
|
+
viewset_class = TestM2MWithDecoratorsViewSet
|
|
340
|
+
related_model = models.TestModelSerializerReverseManyToMany
|
|
341
|
+
related_name = "test_model_serializers"
|
|
342
|
+
api_namespace = "m2m_decorators_test"
|
|
343
|
+
related_names = ["dec_a", "dec_b", "dec_c"]
|
|
344
|
+
|
|
345
|
+
def setUp(self):
|
|
346
|
+
super().setUp()
|
|
347
|
+
# Reset decorator call counts before each test
|
|
348
|
+
_decorator_calls["get"] = 0
|
|
349
|
+
_decorator_calls["post"] = 0
|
|
350
|
+
|
|
351
|
+
async def test_get_decorator_is_applied(self):
|
|
352
|
+
"""Test that get_decorators are applied to the GET endpoint."""
|
|
353
|
+
# First add some related objects
|
|
354
|
+
await self._add_related()
|
|
355
|
+
initial_get_count = _decorator_calls["get"]
|
|
356
|
+
|
|
357
|
+
# Call the GET endpoint
|
|
358
|
+
await self.get_view(request=self.request.get(), pk=self.path_schema)
|
|
359
|
+
|
|
360
|
+
# Verify the get decorator was called
|
|
361
|
+
self.assertEqual(
|
|
362
|
+
_decorator_calls["get"],
|
|
363
|
+
initial_get_count + 1,
|
|
364
|
+
"GET decorator should have been called once",
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
async def test_post_decorator_is_applied(self):
|
|
368
|
+
"""Test that post_decorators are applied to the POST endpoint."""
|
|
369
|
+
initial_post_count = _decorator_calls["post"]
|
|
370
|
+
|
|
371
|
+
# Call the POST endpoint to add related objects
|
|
372
|
+
await self._add_related()
|
|
373
|
+
|
|
374
|
+
# Verify the post decorator was called
|
|
375
|
+
self.assertEqual(
|
|
376
|
+
_decorator_calls["post"],
|
|
377
|
+
initial_post_count + 1,
|
|
378
|
+
"POST decorator should have been called once",
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
async def test_decorators_independent(self):
|
|
382
|
+
"""Test that GET and POST decorators are applied independently."""
|
|
383
|
+
# Reset counts
|
|
384
|
+
_decorator_calls["get"] = 0
|
|
385
|
+
_decorator_calls["post"] = 0
|
|
386
|
+
|
|
387
|
+
# Call POST endpoint
|
|
388
|
+
await self._add_related()
|
|
389
|
+
self.assertEqual(_decorator_calls["post"], 1)
|
|
390
|
+
self.assertEqual(_decorator_calls["get"], 0)
|
|
391
|
+
|
|
392
|
+
# Call GET endpoint
|
|
393
|
+
await self.get_view(request=self.request.get(), pk=self.path_schema)
|
|
394
|
+
self.assertEqual(_decorator_calls["post"], 1) # Still 1
|
|
395
|
+
self.assertEqual(_decorator_calls["get"], 1)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
@tag("many_to_many_api", "decorators", "schema")
|
|
399
|
+
class M2MRelationSchemaDecoratorsFieldTestCase(TestCase):
|
|
400
|
+
"""Test cases for M2MRelationSchema decorator fields."""
|
|
401
|
+
|
|
402
|
+
def test_decorators_default_to_empty_list(self):
|
|
403
|
+
"""Test that decorators default to empty lists."""
|
|
404
|
+
schema = M2MRelationSchema(
|
|
405
|
+
model=models.TestModelSerializerReverseManyToMany,
|
|
406
|
+
related_name="test_model_serializers",
|
|
407
|
+
)
|
|
408
|
+
self.assertEqual(schema.get_decorators, [])
|
|
409
|
+
self.assertEqual(schema.post_decorators, [])
|
|
410
|
+
|
|
411
|
+
def test_decorators_accept_list_of_callables(self):
|
|
412
|
+
"""Test that decorators accept a list of callables."""
|
|
413
|
+
|
|
414
|
+
def custom_decorator(func):
|
|
415
|
+
return func
|
|
416
|
+
|
|
417
|
+
schema = M2MRelationSchema(
|
|
418
|
+
model=models.TestModelSerializerReverseManyToMany,
|
|
419
|
+
related_name="test_model_serializers",
|
|
420
|
+
get_decorators=[custom_decorator],
|
|
421
|
+
post_decorators=[custom_decorator, custom_decorator],
|
|
422
|
+
)
|
|
423
|
+
self.assertEqual(len(schema.get_decorators), 1)
|
|
424
|
+
self.assertEqual(len(schema.post_decorators), 2)
|
|
425
|
+
self.assertEqual(schema.get_decorators[0], custom_decorator)
|
|
426
|
+
|
|
427
|
+
def test_decorators_can_be_none(self):
|
|
428
|
+
"""Test that decorators can explicitly be set to None."""
|
|
429
|
+
schema = M2MRelationSchema(
|
|
430
|
+
model=models.TestModelSerializerReverseManyToMany,
|
|
431
|
+
related_name="test_model_serializers",
|
|
432
|
+
get_decorators=None,
|
|
433
|
+
post_decorators=None,
|
|
434
|
+
)
|
|
435
|
+
# None values should be accepted
|
|
436
|
+
self.assertIsNone(schema.get_decorators)
|
|
437
|
+
self.assertIsNone(schema.post_decorators)
|
|
@@ -138,12 +138,11 @@ class JwtAuthTests(TestCase):
|
|
|
138
138
|
return "should-not-happen"
|
|
139
139
|
|
|
140
140
|
bearer = TB()
|
|
141
|
-
|
|
142
|
-
# Mock jwt.decode to raise ValueError to test that branch
|
|
141
|
+
|
|
143
142
|
import unittest.mock as mock
|
|
144
143
|
|
|
145
144
|
with mock.patch("ninja_aio.auth.jwt.decode") as mock_decode:
|
|
146
|
-
mock_decode.side_effect =
|
|
145
|
+
mock_decode.side_effect = errors.JoseError("invalid token")
|
|
147
146
|
result = async_to_sync(bearer.authenticate)(HttpRequest(), "fake-token")
|
|
148
147
|
self.assertFalse(result)
|
|
149
148
|
|
|
@@ -890,7 +890,7 @@ class DetailSchemaModelSerializerTestCase(TestCase):
|
|
|
890
890
|
"""Test that get_schemas returns a 4-tuple with detail schema."""
|
|
891
891
|
result = self.viewset.get_schemas()
|
|
892
892
|
self.assertEqual(len(result), 4)
|
|
893
|
-
schema_out, schema_detail,
|
|
893
|
+
schema_out, schema_detail, _, _ = result
|
|
894
894
|
self.assertIsNotNone(schema_out)
|
|
895
895
|
self.assertIsNotNone(schema_detail)
|
|
896
896
|
|
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/.github/workflows/coverage.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/api/models/model_serializer.md
RENAMED
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/api/models/serializers.md
RENAMED
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/api/renderers/orjson_renderer.md
RENAMED
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/api/views/api_view_set.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/getting_started/installation.md
RENAMED
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/getting_started/quick_start.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/docs/tutorial/authentication.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/decorators/__init__.py
RENAMED
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/decorators/operations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/ninja_aio/factory/operations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/core/test_exceptions_api.py
RENAMED
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/core/test_renderer_parser.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/models/test_model_util.py
RENAMED
|
File without changes
|
{django_ninja_aio_crud-2.15.0 → django_ninja_aio_crud-2.16.0}/tests/models/test_models_extra.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|