Skip to content

Commit

Permalink
Merge pull request #1261 from scorpp/master
Browse files Browse the repository at this point in the history
Pass serialisation context to model_dump (fixes #1233)
  • Loading branch information
vitalik authored Aug 12, 2024
2 parents d7d25a6 + 387f211 commit f7bc7f8
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 1 deletion.
10 changes: 9 additions & 1 deletion ninja/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from ninja.constants import NOT_SET, NOT_SET_TYPE
from ninja.errors import AuthenticationError, ConfigError, Throttled, ValidationError
from ninja.params.models import TModels
from ninja.schema import Schema
from ninja.schema import Schema, pydantic_version
from ninja.signature import ViewSignature, is_async
from ninja.throttling import BaseThrottle
from ninja.types import DictStrAny
Expand Down Expand Up @@ -261,11 +261,19 @@ def _result_to_response(
resp_object, context={"request": request, "response_status": status}
)

model_dump_kwargs = {}
if pydantic_version >= [2, 7]:
# pydantic added support for serialization context at 2.7
model_dump_kwargs.update(
context={"request": request, "response_status": status}
)

result = validated_object.model_dump(
by_alias=self.by_alias,
exclude_unset=self.exclude_unset,
exclude_defaults=self.exclude_defaults,
exclude_none=self.exclude_none,
**model_dump_kwargs,
)["response"]
return self.api.create_response(
request, result, temporal_response=temporal_response
Expand Down
75 changes: 75 additions & 0 deletions tests/test_serialization_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from unittest import mock

import pytest
from pydantic import model_serializer

from ninja import Router, Schema
from ninja.schema import pydantic_version
from ninja.testing import TestClient


def api_endpoint_test(request):
return {
"test1": "foo",
"test2": "bar",
}


@pytest.mark.skipif(
pydantic_version < [2, 7],
reason="Serialization context was introduced in Pydantic 2.7",
)
def test_request_is_passed_in_context_when_supported():
class SchemaWithCustomSerializer(Schema):
test1: str
test2: str

@model_serializer(mode="wrap")
def ser_model(self, handler, info):
assert "request" in info.context
assert info.context["request"].path == "/test" # check it is HttRequest
assert "response_status" in info.context

return handler(self)

router = Router()
router.add_api_operation(
"/test", ["GET"], api_endpoint_test, response=SchemaWithCustomSerializer
)

TestClient(router).get("/test")


@pytest.mark.parametrize(
["pydantic_version"],
[
[[2, 0]],
[[2, 4]],
[[2, 6]],
],
)
def test_no_serialisation_context_used_when_no_supported(pydantic_version):
class SchemaWithCustomSerializer(Schema):
test1: str
test2: str

@model_serializer(mode="wrap")
def ser_model(self, handler, info):
if hasattr(info, "context"):
# an actually newer Pydantic, but pydantic_version is still mocked, so no context is expected
assert info.context is None

return handler(self)

with mock.patch("ninja.operation.pydantic_version", pydantic_version):
router = Router()
router.add_api_operation(
"/test", ["GET"], api_endpoint_test, response=SchemaWithCustomSerializer
)

resp_json = TestClient(router).get("/test").json()

assert resp_json == {
"test1": "foo",
"test2": "bar",
}

0 comments on commit f7bc7f8

Please sign in to comment.