From eae5d8fed44451d98ebad0a85c2f7ada0f085e33 Mon Sep 17 00:00:00 2001 From: Ezeudoh Tochukwu Date: Mon, 30 Dec 2024 08:43:20 +0100 Subject: [PATCH 1/3] Added support to add extra config dict options for creating ModelSchema --- ninja_extra/controllers/model/schemas.py | 10 ++++++++-- .../test_model_controller_configurations.py | 8 ++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ninja_extra/controllers/model/schemas.py b/ninja_extra/controllers/model/schemas.py index 1733bc47..532c4b80 100644 --- a/ninja_extra/controllers/model/schemas.py +++ b/ninja_extra/controllers/model/schemas.py @@ -54,9 +54,10 @@ class ModelSchemaConfig(PydanticModel): exclude: t.Set[str] = Field(set()) optional: t.Optional[t.Union[str, t.Set[str]]] = Field(default=None) depth: int = 0 - # + read_only_fields: t.Optional[t.List[str]] = Field(default=None) write_only_fields: t.Optional[t.Union[t.List[str]]] = Field(default=None) + extra_config_dict: t.Optional[t.Dict[str, t.Any]] = Field(default=None) class ModelConfig(PydanticModel): @@ -156,6 +157,7 @@ def generate_all_schema(self) -> None: fields=list(create_schema_fields), skip_registry=True, depth=self.schema_config.depth, + **(self.schema_config.extra_config_dict or {}), ) if not self.update_schema and "update" in self.allowed_routes: @@ -166,7 +168,9 @@ def generate_all_schema(self) -> None: working_fields, model_pk ) self.update_schema = SchemaFactory.create_schema( - self.model, fields=list(create_schema_fields) + self.model, + fields=list(create_schema_fields), + **(self.schema_config.extra_config_dict or {}), ) if not self.patch_schema and "patch" in self.allowed_routes: @@ -180,6 +184,7 @@ def generate_all_schema(self) -> None: optional_fields=list(create_schema_fields), skip_registry=True, depth=self.schema_config.depth, + **(self.schema_config.extra_config_dict or {}), ) if not self.retrieve_schema: @@ -192,6 +197,7 @@ def generate_all_schema(self) -> None: fields=list(retrieve_schema_fields), skip_registry=True, depth=self.schema_config.depth, + **(self.schema_config.extra_config_dict or {}), ) def _get_create_schema_fields(self, working_fields: set, model_pk: str) -> set: diff --git a/tests/test_model_controller/test_model_controller_configurations.py b/tests/test_model_controller/test_model_controller_configurations.py index af576f8a..33c88aba 100644 --- a/tests/test_model_controller/test_model_controller_configurations.py +++ b/tests/test_model_controller/test_model_controller_configurations.py @@ -42,6 +42,7 @@ def test_default_model_config(): "depth": 0, "read_only_fields": None, "write_only_fields": None, + "extra_config_dict": None, } assert model_config.create_route_info == {} assert model_config.find_one_route_info == {} @@ -55,7 +56,10 @@ def test_include_gen_schema(): model_config = ModelConfig( model=Event, allowed_routes=["list", "find_one"], - schema_config=ModelSchemaConfig(include=["title", "start_date", "end_date"]), + schema_config=ModelSchemaConfig( + include=["title", "start_date", "end_date"], + extra_config_dict={"title": "EventCustomTitle"}, + ), ) assert model_config.create_schema is None assert model_config.patch_schema is None @@ -83,7 +87,7 @@ def test_include_gen_schema(): }, }, "required": ["id", "title", "start_date", "end_date"], - "title": "EventSchema", + "title": "EventCustomTitle", "type": "object", } From 99cba50d1efb76b8c2e38fa8eec8c9e7b2cf58e1 Mon Sep 17 00:00:00 2001 From: Ezeudoh Tochukwu Date: Mon, 30 Dec 2024 08:52:55 +0100 Subject: [PATCH 2/3] Update ModelController Docs --- docs/api_controller/model_controller.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/api_controller/model_controller.md b/docs/api_controller/model_controller.md index b6a37e1d..2308f2eb 100644 --- a/docs/api_controller/model_controller.md +++ b/docs/api_controller/model_controller.md @@ -42,7 +42,14 @@ from .models import Event class EventModelController(ModelControllerBase): model_config = ModelConfig( model=Event, - schema_config=ModelSchemaConfig(read_only_fields=["id", "category"]), + schema_config=ModelSchemaConfig( + read_only_fields=["id", "category"], + # if you want to extra configuration to the generated schemas + # extra_config_dict={ + # 'title': 'EventCustomTitle', + # 'populate_by_name': True + # } + ), ) api = NinjaExtraAPI() @@ -79,6 +86,7 @@ The `ModelConfig` is a Pydantic schema designed for validating and configuring t - `depth`: The depth for nesting schema generation. - `read_only_fields`: A list of fields to be excluded when generating input schemas for create, update, and patch operations. - `write_only_fields`: A list of fields to be excluded when generating output schemas for find_one and list operations. + - `extra_config_dict`: A dictionary of extra configuration to be added to the generated schemas. Options must be valid Pydantic configuration options. - **pagination**: A requisite for the model `list/GET` operation to prevent sending `100_000` items at once in a request. The pagination configuration mandates a `ModelPagination` Pydantic schema object for setup. Options encompass: - `klass`: The pagination class of type `PaginationBase`. The default is `PageNumberPaginationExtra`. - `paginator_kwargs`: A dictionary value for `PaginationBase` initialization. The default is None. @@ -305,8 +313,10 @@ class EventModelController(ModelControllerBase): custom_handler=lambda self, data, **kw: self.handle_add_event_to_new_category(data, **kw) ) - def handle_add_event_to_new_category(self, data: CreateCategorySchema, **kw: Any) -> Category: - event = self.service.get_one(pk=kw['event_id']) + def handle_add_event_to_new_category( + self, data: CreateCategorySchema, event_id: int, **kw: Any + ) -> Category: + event = self.service.get_one(pk=event_id) category = Category.objects.create(title=data.title) event.category = category event.save() @@ -361,8 +371,10 @@ class EventModelController(ModelControllerBase): custom_handler=lambda self, data, **kw: self.handle_add_event_to_new_category(data, **kw) ) - async def handle_add_event_to_new_category(self, data: CreateCategorySchema, **kw: Any) -> Category: - event = await self.service.get_one_async(pk=kw['event_id']) + async def handle_add_event_to_new_category( + self, data: CreateCategorySchema, event_id: int, **kw: Any + ) -> Category: + event = await self.service.get_one_async(pk=event_id) category = Category.objects.create(title=data.title) event.category = category event.save() From 63517ed06f48eb91e9bfac25f627de4eca9291f7 Mon Sep 17 00:00:00 2001 From: Ezeudoh Tochukwu Date: Mon, 30 Dec 2024 09:15:10 +0100 Subject: [PATCH 3/3] upgraded ninja-schema to latest --- ninja_extra/controllers/model/schemas.py | 14 ++++++++++++++ requirements-tests.txt | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/ninja_extra/controllers/model/schemas.py b/ninja_extra/controllers/model/schemas.py index 532c4b80..06805da5 100644 --- a/ninja_extra/controllers/model/schemas.py +++ b/ninja_extra/controllers/model/schemas.py @@ -1,11 +1,13 @@ import typing as t +from django.core.exceptions import ImproperlyConfigured from django.db.models import Model from ninja.pagination import PaginationBase from pydantic import BaseModel as PydanticModel from pydantic import Field, field_validator try: + from ninja_schema import __version__ as ninja_schema_version from ninja_schema.errors import ConfigError from ninja_schema.orm.factory import SchemaFactory from ninja_schema.orm.model_schema import ( @@ -14,15 +16,24 @@ from ninja_schema.orm.model_schema import ( ModelSchemaConfigAdapter, ) + + NINJA_SCHEMA_VERSION = tuple(map(int, ninja_schema_version.split("."))) except Exception: # pragma: no cover ConfigError = NinjaSchemaModelSchemaConfig = ModelSchemaConfigAdapter = ( SchemaFactory ) = None + NINJA_SCHEMA_VERSION = (0, 0, 0) from ninja_extra.pagination import PageNumberPaginationExtra, PaginatedResponseSchema +def _is_ninja_schema_version_supported() -> bool: + if NINJA_SCHEMA_VERSION[1] >= 14 and NINJA_SCHEMA_VERSION[2] >= 1: + return True + raise ImproperlyConfigured("ninja-schema version 0.14.1 or higher is required") + + class ModelPagination(PydanticModel): """ Model Controller Pagination Configuration @@ -147,6 +158,9 @@ def generate_all_schema(self) -> None: exclude_fields = set(self.schema_config.exclude) working_fields = working_fields - exclude_fields + if self.schema_config.extra_config_dict: + _is_ninja_schema_version_supported() + if not self.create_schema and "create" in self.allowed_routes: create_schema_fields = self._get_create_schema_fields( working_fields, model_pk diff --git a/requirements-tests.txt b/requirements-tests.txt index 92743043..77c26569 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -1,6 +1,6 @@ django-stubs mypy == 1.13.0 -ninja-schema>=0.14.0 +ninja-schema>=0.14.1 pytest pytest-asyncio==0.24.0 pytest-cov