-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
anyOf
causing problems with Swagger/ReDoc on primitive types
#6647
Comments
You missed the anyOf in ValidationError. |
I've fixed it. Thanks 🙏
Ah, ok. Then 2 issues here. 😅 |
Currently data:Literal["dog"] | None schema:
anyOf:
- const: dog
- type: 'null' Due to the known problems I'd unfold to enum instead of const schema:
type: [string, null]
enum: ["dog", null] The type can be skipped. schema:
enum: ["dog", null] Skipping the type would even work for mixed enums data: Literal["a",1,True]|None schema:
- enum:
- a
- 1
- true
- null |
We have a solution using Pydantic only: #6653. We are also working on a solution for FastAPI without the need of |
Similar issue where previously from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class RequestModel(BaseModel):
name: str
age: Optional[int]
@app.get("/")
def index(req: RequestModel):
return {"message": "Hello World"} When using pydantic v1 it returns this OpenAPI schema for RequestModel "RequestModel": {
"properties": {
"name": {
"type": "string",
"title": "Name"
},
"age": {
"type": "integer",
"title": "Age"
}
},
"type": "object",
"required": [
"name"
],
"title": "RequestModel"
}, When using pydantic v2 it returns this OpenAPI schema for RequestModel "RequestModel": {
"properties": {
"name": {
"type": "string",
"title": "Name"
},
"age": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Age"
}
},
"type": "object",
"required": [
"name",
"age"
],
"title": "RequestModel"
}, I understand that both are basically the same meaing for my purposes however I have code generation that runs off the OpenAPI schema and the former generates better TypeScript code. Is there a way to generate the former? |
Refer to https://docs.pydantic.dev/latest/migration/#required-optional-and-nullable-fields to adjust your model according to your expectation. If you avoid nullable, you won't get the anyOf. |
@commonism are you suggesting the way to do it would be |
v2: |
I think the solution of modifying FastAPI to use a custom However, once #6798 is merged, it will be possible to do: import json
from typing import TYPE_CHECKING, Annotated, Union
from pydantic import BaseModel, Field
from pydantic.json_schema import SkipJsonSchema
if TYPE_CHECKING:
from typing import Optional as OptionalParam
else:
class OptionalParam:
def __class_getitem__(cls, item):
return Annotated[
Union[item, SkipJsonSchema[None]],
Field(json_schema_extra=lambda x: x.pop('default', None))
] which will then let you do: class MyModel(BaseModel):
x: OptionalParam[int] = None
y: OptionalParam[int]
print(json.dumps(MyModel.model_json_schema(), indent=2))
"""
{
"properties": {
"x": {
"title": "X",
"type": "integer"
},
"y": {
"title": "Y",
"type": "integer"
}
},
"required": [
"y"
],
"title": "MyModel",
"type": "object"
}
"""
# output from mypy:
reveal_type(MyModel.x)
# note: Revealed type is "Union[builtins.int, None]"
reveal_type(MyModel.y)
# note: Revealed type is "Union[builtins.int, None]" which will get you a JSON schema / OpenAPI schema that looks the way it did with Pydantic V1. (And as you can see, still type-checks properly.) Given this, I think after merging that PR it will make sense to close this issue in Pydantic, and instead open a FastAPI issue for the bug (and/or just wait/hope for tiangolo/fastapi#9873 to be merged). |
I consider the quality of the description document the key selling point of pydantic, and as I said - this breaks it. So let's call it CompatibleOptional, document the shortcomings, and emit "x-nullable: True" as Schema Extension in the dd to document the skipped constraints. |
Did you try to generate a Swagger with a schema that contains array as |
Use https://editor-next.swagger.io/ For
Splitting
into
requires some kind of folding for the models to get back to the original form of Having the description document compiler emit structural … difficult to deal with … model definitions does not make it a good choice due to the complexity in the models on the consumers side. |
Can you add some endpoints that use those? I've asked because there were UI issues with it. Your example only has the components. |
Rendering is borked for |
I'll just note that if we merge #6951, it will require a subtle change to my previously-suggested code for this. from pydantic import BaseModel, Field
from pydantic.json_schema import SkipJsonSchema
class Model(BaseModel):
x: int | SkipJsonSchema[None] = Field(default=None, json_schema_extra=lambda x: x.pop('default'))
print(Model.model_json_schema())
#> {'properties': {'x': {'title': 'X', 'type': 'integer'}}, 'title': 'Model', 'type': 'object'} In particular, what used to be: x: Annotated[int | SkipJsonSchema[None], Field(json_schema_extra=lambda x: x.pop('default'))] = None should now be x: int | SkipJsonSchema[None] = Field(default=None, json_schema_extra=lambda x: x.pop('default')) I think this is actually more sensible anyway since I have updated the PR body for #6798 to reflect this in case anyone goes looking for it in the future. |
Initial Checks
Description
If we have an object, and a primite type, we are still creating an
anyOf
. That unfortunately doesn't work well with neither ReDoc nor Swagger.From the code below, the following OpenAPI spec is generated:
Details
Ideally, we'd have the following schema:
Details
As you can see above, the
type
becomes an array of primitive types. This is mentioned in the Assertions and Instance Primitive Types on the JSON Schema Draft 2020-12.Example Code
Python, Pydantic & OS Version
Ref.:
Selected Assignee: @dmontagu
The text was updated successfully, but these errors were encountered: