django-ninja
django-ninja copied to clipboard
[BUG] Pydantic exclude=True is respected when generating the response, but not when defining the OpenAPI Schema
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 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
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