For bugs/questions:
Does pydantic currently support polymorphism/inheritance usingallOf? For example if I have the following code (taken from https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#models-with-composition):
from pydantic import BaseModel, Schema
def Error(BaseModel):
message: str = Schema(..., description="")
code: int = Schema(..., description="")
def ExtendedErrorModel(Error):
rootCause: str= Schema(..., description="")
If I were to generate the schema for ExtendedErrorModel I should get something that matches the following which uses allOf to signify that if I'm creating an ExtendedErrorModel object, it not only specifies rootCause but also code and message :
components:
schemas:
ErrorModel:
type: object
required:
- message
- code
properties:
message:
type: string
code:
type: integer
minimum: 100
maximum: 600
ExtendedErrorModel:
allOf:
- $ref: '#/components/schemas/ErrorModel'
- type: object
required:
- rootCause
properties:
rootCause:
type: string
However when it's actually generated, everything is flattened so the properties from the ErrorModel appear regularly on the ExtendedErrorModel. Which is how it should validate it, but not how the OpenAPI schema should appear:
components:
schemas:
ErrorModel:
type: object
required:
- message
- code
- rootCause
properties:
message:
type: string
code:
type: integer
minimum: 100
maximum: 600
rootCause:
type: string
Question for @tiangolo. He's the expert on schema.
Thanks for tagging me @samuelcolvin .
@bquestions, so, generating schemas with inheritance using allOf as you suggest, is currently not supported. Inheritance of Pydantic models is supported and works, but the generated JSON Schema is flattened as you have noticed.
I think implementing it would be non-trivial, and I'm not sure if there would be any advantage of having it.
For example, FastAPI is based on Pydantic, built around OpenAPI, it generates OpenAPI schemas automatically, etc. And using the current implementation of Pydantic models has worked for most of the use cases (all the ones I know).
On the other side, if it was implemented that way, I'm not sure it would be the best option. For example, let's say a client gets to see a User model on the OpenAPI schema, and this model inherited from UserInDB, and that model, in turn, inherited from UserBase. In this case, the client would have to deal with 3 models/schemas, in a chain of inheritance. While he actually just cared about the User model, the other models are never even used by him but by the backend directly. And still he would have to deal with multiple inheritance, etc.
Thank you for the response @tiangolo. The problem is that in the project I'm working on, we have base schema definitions from one library that are being extended in another library so being able to ref in an allOf would be helpful if one wanted to see the parent schema for the object vs creating a new object. In your example, instead of seeing the chain from User --> UserInDB --> UserBase you'd have this brand new schema/model called User vs that the User builds from other schemas (similar to the example in the OpenAPI docs).
If I'm understanding OpenAPI schemas correctly, wouldn't seeing that progression be useful since we are extending models vs duplicating schema properties on similar models vs just using the allOf property? In terms of functionality I want to write the pydantic schemas how they currently are (ie in my example), I just want the schemas to display using allOf if that's possible
Hmm, I see @bquestions , it's currently not possible :disappointed:.
I think the implementation would be non-trivial, it would probably require fiddling with the Python metaclass to inspect the inheritance chain, etc. And I guess it would have to be optional, to support the default case. But maybe you or someone else wants to take a stab at it and start a PR :grin: .
Lol yeah sure, I can take a look into a potential solution when I have some free time this weekend to see if it would work.
Thank you!
@bquestions You might be able to implement a custom validator and use schema_extra (recently added) to override the JSON Schema. It might work for your use case.
On the other side, it seems allOf might not be necessarily appropriate for inheritance:
allOf can not be used to โextendโ a schema to add more details to it in the sense of object-oriented inheritance.
That's because in JSON schema, what would be a "subclass" (in allOf) wouldn't be able to override something already declared in other class. So, a subclass couldn't declare an attribute as optional if the parent class already declared it as required. Even though you can do it in OO inheritance and in Pydantic.
I think we can close this issue as it's specifically related to allOf in the context of inheritance.
If later we talk about allOf in another context, it would be better in a new issue, to avoid confusion.
Most helpful comment
Lol yeah sure, I can take a look into a potential solution when I have some free time this weekend to see if it would work.
Thank you!