Skip to content

Commit 4a51ecf

Browse files
authored
uuidstr url converter instead of shadowing django uuid (#1453)
uuidstr url converter instead of shadowing django uuid
1 parent 932d22b commit 4a51ecf

File tree

6 files changed

+30
-16
lines changed

6 files changed

+30
-16
lines changed

ninja/router.py

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import re
12
from typing import (
23
TYPE_CHECKING,
34
Any,
@@ -320,6 +321,12 @@ def add_api_operation(
320321
include_in_schema: bool = True,
321322
openapi_extra: Optional[Dict[str, Any]] = None,
322323
) -> None:
324+
path = re.sub(r"\{uuid:(\w+)\}", r"{uuidstr:\1}", path, flags=re.IGNORECASE)
325+
# django by default convert strings to UUIDs
326+
# but we want to keep them as strings to let pydantic handle conversion/validation
327+
# if user whants UUID object
328+
# uuidstr is custom registered converter
329+
323330
if path not in self.path_operations:
324331
path_view = PathView()
325332
self.path_operations[path] = path_view

ninja/signature/details.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ def is_pydantic_model(cls: Any) -> bool:
289289
if get_origin(cls) in UNION_TYPES:
290290
return any(issubclass(arg, pydantic.BaseModel) for arg in get_args(cls))
291291
return issubclass(cls, pydantic.BaseModel)
292-
except TypeError:
292+
except TypeError: # pragma: no cover
293293
return False
294294

295295

ninja/signature/utils.py

+4-9
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,11 @@ def get_args_names(func: Callable[..., Any]) -> List[str]:
7272
return list(inspect.signature(func).parameters.keys())
7373

7474

75-
class NinjaUUIDConverter:
75+
class UUIDStrConverter(UUIDConverter):
7676
"""Return a path converted UUID as a str instead of the standard UUID"""
7777

78-
regex = UUIDConverter.regex
78+
def to_python(self, value: str) -> str: # type: ignore
79+
return value # return string value instead of UUID
7980

80-
def to_python(self, value: str) -> str:
81-
return value
8281

83-
def to_url(self, value: Any) -> str:
84-
return str(value)
85-
86-
87-
register_converter(NinjaUUIDConverter, "uuid")
82+
register_converter(UUIDStrConverter, "uuidstr")

tests/main.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,15 @@ def get_path_param_django_uuid(request, item_id: UUID):
174174
return item_id
175175

176176

177-
@router.get("/path/param-django-uuid-str/{uuid:item_id}")
178-
def get_path_param_django_uuid_str(request, item_id):
177+
@router.get("/path/param-django-uuid-notype/{uuid:item_id}")
178+
def get_path_param_django_uuid_notype(request, item_id):
179+
# no type annotation defaults to str..............^
180+
assert isinstance(item_id, str)
181+
return item_id
182+
183+
184+
@router.get("/path/param-django-uuid-typestr/{uuid:item_id}")
185+
def get_path_param_django_uuid_typestr(request, item_id: str):
179186
assert isinstance(item_id, str)
180187
return item_id
181188

tests/test_misc.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from ninja import NinjaAPI
88
from ninja.constants import NOT_SET
99
from ninja.signature.details import is_pydantic_model
10-
from ninja.signature.utils import NinjaUUIDConverter
10+
from ninja.signature.utils import UUIDStrConverter
1111
from ninja.testing import TestClient
1212

1313

@@ -48,7 +48,7 @@ def operation(request, a: str, *args, **kwargs):
4848

4949

5050
def test_uuid_converter():
51-
conv = NinjaUUIDConverter()
51+
conv = UUIDStrConverter()
5252
assert isinstance(conv.to_url(uuid.uuid4()), str)
5353

5454

tests/test_path.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -301,12 +301,17 @@ def test_get_path(path, expected_status, expected_response):
301301
"31ea378c-c052-4b4c-bf0b-679ce5cfcc2a",
302302
),
303303
(
304-
"/path/param-django-uuid/31ea378c-c052-4b4c-bf0b-679ce5cfcc2",
304+
"/path/param-django-uuid/31ea378c-c052-4b4c-bf0b-679ce5cfcc2", # invalid UUID (missing last digit)
305305
"Cannot resolve",
306306
Exception,
307307
),
308308
(
309-
"/path/param-django-uuid-str/31ea378c-c052-4b4c-bf0b-679ce5cfcc2a",
309+
"/path/param-django-uuid-notype/31ea378c-c052-4b4c-bf0b-679ce5cfcc2a",
310+
200,
311+
"31ea378c-c052-4b4c-bf0b-679ce5cfcc2a",
312+
),
313+
(
314+
"/path/param-django-uuid-typestr/31ea378c-c052-4b4c-bf0b-679ce5cfcc2a",
310315
200,
311316
"31ea378c-c052-4b4c-bf0b-679ce5cfcc2a",
312317
),

0 commit comments

Comments
 (0)