django-ninja icon indicating copy to clipboard operation
django-ninja copied to clipboard

[BUG] Pydantic exclude=True is respected when generating the response, but not when defining the OpenAPI Schema

Open shmulvad opened this issue 1 year ago • 2 comments
trafficstars

Describe the bug

Consider the following Pydantic model:

from ninja import Schema, Field

class MySchema(Schema):
    date1: date | None = Field(None, exclude=True)
    date2: date | None = Field(None, exclude=True)
    first_date: date | None = None

    def model_post_init(self, *args: Any) -> None:
        self.first_date = min((d for d in [self.date1, self.date2] if d is not None), default=None)

If I set MySchema as the output schema for an endpoint in Django Ninja, the response at runtime will correctly exclude the date1 and date2 fields. However, when viewing the defined schema in the OpenAPI doc, these two fields are still included. I think it would make more sense if they are excluded from the schema since they can never be included.

I know about the exlude_unset, exclude_none, and exclude_defaults arguments when defining an endpoint, but I don't believe they would solve this as they only change behaviour at endpoint runtime. This is for changing the behaviour at the OpenAPI schema generation time.

Versions:

  • Python version: 3.12.4
  • Django version: 5.0.7
  • Django-Ninja version: 1.2.1
  • Pydantic version: 2.8.2

shmulvad avatar Jul 20 '24 05:07 shmulvad

@shmulvad well docs say that indeed exclude only applies to serialization

maybe for you case you can overwrite model_json_schema method:

class MySchema(Schema):
  ...

  def model_json_schema(self, by_alias: bool = True) -> dict[str, Any]:
        schema = super().model_json_schema(by_alias=by_alias)
        # Modify the schema here to exclude specific fields
        fields_to_exclude = ['date1', 'date2']  # Fields you want to exclude from the schema
        for field in fields_to_exclude:
            schema.get("properties", {}).pop(field, None)
        return schema

vitalik avatar Aug 20 '24 14:08 vitalik

Another alternative is to use pydantics SkipJsonSchema type annotation like so

from ninja import Schema, Field
from pydantic.json_schema import SkipJsonSchema

class MySchema(Schema):
    date1: SkipJsonSchema[date | None] = Field(None, exclude=True)
    date2: SkipJsonSchema[date | None] = Field(None, exclude=True)
    first_date: date | None = None

    def model_post_init(self, *args: Any) -> None:
        self.first_date = min((d for d in [self.date1, self.date2] if d is not None), default=None)

Would be nice if this was a setting on the Field class itself

lroling8350 avatar Apr 24 '25 14:04 lroling8350