Skip to content

Commit 72cf227

Browse files
committed
Added ModelEndpointFactory for more dynamic control over model controller
1 parent 89af5f2 commit 72cf227

File tree

8 files changed

+633
-131
lines changed

8 files changed

+633
-131
lines changed

ninja_extra/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
ModelConfig,
1010
ModelControllerBase,
1111
ModelControllerBuilder,
12+
ModelEndpointFactory,
1213
ModelPagination,
1314
ModelSchemeConfig,
1415
ModelService,
@@ -61,4 +62,5 @@
6162
"ModelPagination",
6263
"ModelServiceBase",
6364
"ModelControllerBase",
65+
"ModelEndpointFactory",
6466
]

ninja_extra/controllers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from .model import (
33
ModelConfig,
44
ModelControllerBuilder,
5+
ModelEndpointFactory,
56
ModelPagination,
67
ModelSchemeConfig,
78
ModelService,
@@ -47,4 +48,5 @@
4748
"ModelControllerBuilder",
4849
"ModelPagination",
4950
"ModelServiceBase",
51+
"ModelEndpointFactory",
5052
]

ninja_extra/controllers/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ def build_routers(self) -> List[Tuple[str, "APIController"]]:
389389

390390
def add_controller_route_function(self, route_function: RouteFunction) -> None:
391391
self._controller_class_route_functions[
392-
get_function_name(route_function.route.view_func)
392+
f"{get_function_name(route_function.route.view_func)}_{route_function.route.route_params.path}"
393393
] = route_function
394394

395395
def urls_paths(self, prefix: str) -> Iterator[URLPattern]:

ninja_extra/controllers/model/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .builder import ModelControllerBuilder
2+
from .endpoints import ModelEndpointFactory
23
from .interfaces import ModelServiceBase
34
from .schemas import ModelConfig, ModelPagination, ModelSchemeConfig
45
from .service import ModelService
@@ -10,4 +11,5 @@
1011
"ModelSchemeConfig",
1112
"ModelPagination",
1213
"ModelControllerBuilder",
14+
"ModelEndpointFactory",
1315
]

ninja_extra/controllers/model/builder.py

Lines changed: 44 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
import typing as t
22

33
from ninja.orm.fields import TYPES
4-
from ninja.params import Body, Path
54

65
from ninja_extra.constants import ROUTE_FUNCTION
76

8-
from ... import status
9-
from ...exceptions import NotFound
10-
from ...pagination import paginate
11-
from ..route import route
7+
from .endpoints import ModelEndpointFactory
128
from .schemas import ModelConfig
139

1410
try:
@@ -155,178 +151,102 @@ def _add_to_controller(self, func: t.Callable) -> None:
155151
self._api_controller_instance.add_controller_route_function(route_function)
156152

157153
def _register_create_endpoint(self) -> None:
158-
create_schema_in = self._config.create_schema
159-
create_schema_out = self._retrieve_schema
160-
model_name = self._model_name
161-
162-
@route.post(
163-
"/",
164-
response={201: create_schema_out},
154+
create_item = ModelEndpointFactory.create(
155+
schema_in=self._create_schema, # type: ignore[arg-type]
156+
schema_out=self._retrieve_schema, # type: ignore[arg-type]
165157
url_name=f"{self._model_name}-create",
166-
description=f"Create {model_name} item",
158+
description=f"Create {self._model_name} item",
159+
summary="Create an item",
167160
)
168-
def create_item(
169-
self: "ModelControllerBase",
170-
data: create_schema_in = Body(default=...), # type:ignore[valid-type]
171-
) -> t.Any:
172-
instance = self.service.create(data)
173-
assert instance, "`service.create` must return a value"
174-
return instance
175161

176162
self._add_to_controller(create_item)
177163

178164
def _register_update_endpoint(self) -> None:
179-
update_schema_in = self._update_schema
180-
update_schema_out = self._retrieve_schema
181-
182-
_pk_type = self._pk_type
183165
_path = "/{%s:%s}" % (
184-
_pk_type.__name__,
166+
self._pk_type.__name__,
185167
self._model_pk_name,
186168
)
187-
model_name = self._model_name
188-
model_pk_name = self._model_pk_name
189-
190-
@route.put(
191-
_path,
192-
response={200: update_schema_out},
193-
url_name=f"{model_pk_name}-put",
194-
description=f"""Update {model_name} item by {model_pk_name}""",
169+
170+
update_item = ModelEndpointFactory.update(
171+
path=_path,
172+
lookup_field=self._model_pk_name,
173+
schema_in=self._update_schema, # type: ignore[arg-type]
174+
schema_out=self._retrieve_schema, # type: ignore[arg-type]
175+
url_name=f"{self._model_pk_name}-put",
176+
description=f"""Update {self._model_name} item by {self._model_pk_name}""",
177+
summary="Update an item",
195178
)
196-
def update_item(
197-
self: "ModelControllerBase",
198-
pk: _pk_type = Path( # type:ignore[valid-type]
199-
default=..., alias=model_pk_name
200-
),
201-
data: update_schema_in = Body(default=...), # type:ignore[valid-type]
202-
) -> t.Any:
203-
obj = self.service.get_one(pk=pk)
204-
if not obj:
205-
raise NotFound()
206-
self.check_object_permissions(obj)
207-
instance = self.service.update(instance=obj, schema=data)
208-
assert instance, "`service.update` must return a value"
209-
return instance
210179

211180
self._add_to_controller(update_item)
212181

213182
def _register_patch_endpoint(self) -> None:
214-
update_schema_in = self._patch_schema
215-
update_schema_out = self._retrieve_schema
216-
217183
_pk_type = self._pk_type
218184
_path = "/{%s:%s}" % (
219185
_pk_type.__name__,
220186
self._model_pk_name,
221187
)
222-
model_name = self._model_name
223-
model_pk_name = self._model_pk_name
224188

225-
@route.patch(
226-
_path,
227-
response={200: update_schema_out},
189+
patch_item = ModelEndpointFactory.patch(
190+
path=_path,
191+
lookup_field=self._model_pk_name,
192+
schema_out=self._retrieve_schema, # type: ignore[arg-type]
193+
schema_in=self._patch_schema, # type: ignore[arg-type]
228194
url_name=f"{self._model_pk_name}-patch",
229-
description=f"""Patch {model_name} item by {model_pk_name}""",
195+
description=f"""Patch {self._model_name} item by {self._model_pk_name}""",
196+
summary="Patch an item",
230197
)
231-
def patch_item(
232-
self: "ModelControllerBase",
233-
pk: _pk_type = Path( # type:ignore[valid-type]
234-
default=..., alias=self._model_pk_name
235-
),
236-
data: update_schema_in = Body(default=...), # type:ignore[valid-type]
237-
) -> t.Any:
238-
obj = self.service.get_one(pk=pk)
239-
if not obj:
240-
raise NotFound()
241-
self.check_object_permissions(obj)
242-
instance = self.service.patch(instance=obj, schema=data)
243-
assert instance, "`perform_patch()` must return a value"
244-
return instance
245198

246199
self._add_to_controller(patch_item)
247200

248201
def _register_find_one_endpoint(self) -> None:
249-
retrieve_schema = self._config.retrieve_schema
250-
_pk_type = self._pk_type
251202
_path = "/{%s:%s}" % (
252-
_pk_type.__name__,
203+
self._pk_type.__name__,
253204
self._model_pk_name,
254205
)
255-
model_name = self._model_name
256-
model_pk_name = self._model_pk_name
257206

258-
@route.get(
259-
_path,
260-
response={200: retrieve_schema},
207+
get_item = ModelEndpointFactory.retrieve(
208+
path=_path,
209+
lookup_field=self._model_pk_name,
210+
schema_out=self._retrieve_schema, # type: ignore[arg-type]
261211
url_name=f"{self._model_pk_name}-get-item",
262-
description=f"""Get {model_name} item by {model_pk_name}""",
212+
description=f"""Get {self._model_name} item by {self._model_pk_name}""",
213+
summary="Get a specific item",
263214
)
264-
def get_item(
265-
self: "ModelControllerBase",
266-
pk: _pk_type = Path( # type:ignore[valid-type]
267-
default=..., alias=self._model_pk_name
268-
),
269-
) -> t.Any:
270-
obj = self.service.get_one(pk=pk)
271-
if not obj:
272-
raise NotFound()
273-
self.check_object_permissions(obj)
274-
return obj
275215

276216
self._add_to_controller(get_item)
277217

278218
def _register_list_endpoint(self) -> None:
279219
paginate_kwargs: t.Dict[str, t.Any] = {}
280-
pagination_response_schema = self._config.pagination.pagination_schema
281-
pagination_class = self._config.pagination.klass
282-
retrieve_schema = self._retrieve_schema
283-
model_name = self._model_name
284220

285221
if self._config.pagination.paginate_by:
286222
paginate_kwargs.update(page_size=self._config.pagination.paginate_by)
287223

288-
@route.get(
289-
"/",
290-
response={
291-
200: pagination_response_schema[retrieve_schema] # type:ignore[index]
292-
},
224+
list_items = ModelEndpointFactory.list(
225+
path="/",
226+
schema_out=self._retrieve_schema, # type: ignore[arg-type]
227+
pagination_class=self._config.pagination.klass,
228+
pagination_response_schema=self._config.pagination.pagination_schema,
229+
description=f"List {self._model_name} model items",
293230
url_name=f"{self._model_pk_name}-list",
294-
description=f"List {model_name} model items",
231+
summary="List Items",
232+
**paginate_kwargs,
295233
)
296-
@paginate(pagination_class, **paginate_kwargs)
297-
def list_items(self: "ModelControllerBase") -> t.Any:
298-
return self.service.get_all()
299234

300235
self._add_to_controller(list_items)
301236

302237
def _register_delete_endpoint(self) -> None:
303-
_pk_type = self._pk_type
304238
_path = "/{%s:%s}" % (
305-
_pk_type.__name__,
239+
self._pk_type.__name__,
306240
self._model_pk_name,
307241
)
308-
model_name = self._model_name
309242

310-
@route.delete(
311-
_path,
243+
delete_item = ModelEndpointFactory.delete(
244+
path=_path,
245+
lookup_field=self._model_pk_name,
312246
url_name=f"{self._model_pk_name}-delete",
313-
response={204: str},
314-
description=f"""Delete {model_name} item""",
247+
description=f"""Delete {self._model_name} item""",
248+
summary="Delete an item",
315249
)
316-
def delete_item(
317-
self: "ModelControllerBase",
318-
pk: _pk_type = Path( # type:ignore[valid-type]
319-
default=..., alias=self._model_pk_name
320-
),
321-
) -> t.Any:
322-
obj = self.service.get_one(pk=pk)
323-
if not obj:
324-
raise NotFound()
325-
self.check_object_permissions(obj)
326-
self.service.delete(instance=obj)
327-
return self.create_response(
328-
message="", status_code=status.HTTP_204_NO_CONTENT
329-
)
330250

331251
self._add_to_controller(delete_item)
332252

0 commit comments

Comments
 (0)