Skip to content

Conversation

@camuthig
Copy link

When generating JSON schemas from the Pydantic types, the mode must be set in order for the correct aliases to be used. The default is to generate the schema for validation mode, which means that for building response schemas, the mode needs to be explicitly set to serialization.

This fixes issues I brought up in discussion #1132

When generating JSON schemas from the Pydantic types, the `mode` must be
set in order for the corret aliases to be used. The default is to
generate the schema for validation mode, which means that for building
response schemas, the mode needs to be explicitly set to serialization.
@pmdevita
Copy link
Contributor

This might be the same thing as #1139

@camuthig
Copy link
Author

@pmdevita thanks for calling that out. There is some duplication here in that both pulls depend on leveraging the mode argument in model_json_schema. However, the other issue/pull request are also tackling a more nuanced issue around definition of "required" field.

I'm open to closing the pull request, but considering the other one is also facing some open questions of backwards compatibility, I don't want to slow down this fix because of those unrelated topics.

@nstonic
Copy link

nstonic commented Aug 29, 2024

@camuthig So, what about this PR?

@camuthig
Copy link
Author

@nstonic I'm not sure what the situation here is. @vitalik would you consider this pull request as an simpler version of the referenced #1139, that one is tackling a much deeper problem. This pull request has a smaller scope and only makes sure that the alias configurations are respected in the JSON schema generation. I think the other PR can still be considered as a progression of what this one accomplishes.

@tu-pm
Copy link

tu-pm commented Oct 2, 2024

This also fixes the problem of computed fields missing from the schema, as reported by issue #1292 and #960

@oubeichen
Copy link

@vitalik any update on this?

@rayansostenes
Copy link

rayansostenes commented Dec 3, 2024

Had to solve this at work, here's the monkey patch I applied to make it work.

# pyright: basic
from http.client import responses
from typing import Any

import ninja
import pydantic
from ninja.constants import NOT_SET
from ninja.openapi.schema import REF_TEMPLATE
from ninja.openapi.schema import OpenAPISchema
from ninja.schema import NinjaGenerateJsonSchema
from pydantic.json_schema import JsonSchemaMode


def _create_schema_from_model(
    self,
    model: pydantic.BaseModel,
    by_alias: bool = True,  # noqa: FBT001, FBT002
    remove_level: bool = True,  # noqa: FBT001, FBT002
    mode: JsonSchemaMode = "validation",
) -> tuple[dict[str, Any], bool]:
    if hasattr(model, "__ninja_flatten_map__"):
        schema = self._flatten_schema(model)
    else:
        schema = model.model_json_schema(
            ref_template=REF_TEMPLATE,
            by_alias=by_alias,
            schema_generator=NinjaGenerateJsonSchema,
            mode=mode,
        ).copy()

    # schema = _rewrite_schema(self.schemas, schema, mode)

    if schema.get("$defs"):
        self.add_schema_definitions(schema.pop("$defs"))

    if remove_level and len(schema["properties"]) == 1:
        name, details = next(iter(schema["properties"].items()))

        required = name in schema.get("required", {})
        return details, required
    return schema, True


def _responses(self, operation: Any) -> dict[int, dict[str, Any]]:
    assert bool(operation.response_models), f"{operation.response_models} empty"

    result = {}
    for status, model in operation.response_models.items():
        if status == Ellipsis:
            continue  # it's not yet clear what it means if user wants to output any other code

        description = responses.get(status, "Unknown Status Code")
        details: dict[int, Any] = {status: {"description": description}}
        if model not in [None, NOT_SET]:
            schema = self._create_schema_from_model(
                model,
                by_alias=operation.by_alias,
                mode="serialization",
            )[0]
            details[status]["content"] = {self.api.renderer.media_type: {"schema": schema}}
        result.update(details)

    return result


def apply_ninja_monkey_patch():
    version_tuple = tuple(map(int, ninja.__version__.split(".")))
    if version_tuple > (1, 3, 0):
        msg = "This monkey patch is only tested for ninja<=1.3.0"
        raise ImportError(msg)
    OpenAPISchema._create_schema_from_model = _create_schema_from_model  # noqa: SLF001
    OpenAPISchema.responses = _responses
# settings.py
# ...
from my_package.ninja_api_schema_monkey_patch import apply_ninja_monkey_patch
apply_ninja_monkey_patch()

@camuthig
Copy link
Author

@vitalik bumping this back to your attention. Is there any chance of getting this review and merged in?

@sghng
Copy link

sghng commented Jun 15, 2025

This might be related to #1485, which is about using Pydantic builtin model_dump_json method to serialize responses. Currently it seems like the the Pydantic configs are not respected in serialization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants