OpenAPI does not include properties to an embedded object
alg opened this issue · comments
Describe the bug
It was working nicely in sanic_openapi, but now as we attempt moving to sanic-ext it does not.
When a schema embeds another in a field as an object, it's not embedded. Instead it embeds an object without properties.
When embedded as an array of objects it works as expected.
Screenshots
list_categories
:
get_category
:
To Reproduce
class Category:
id = doc.Integer(required=True, example="1")
name = doc.String(required=True, example="Full-time")
instruction = doc.String(example="Category instruction template")
class CategoryResponse:
data = Category
class CategoriesResponse:
data = [Category]
@app.get("/api/categories")
@doc.response(200, {"application/json": CategoriesResponse}, "Success")
async def list_categories(_request):
...
@app.get("/api/categories/<category_id:int>")
@doc.response(200, {"application/json": CategoryResponse}, "Success")
async def get_category(_request, category_id: int):
...
Expected behavior
Embedded schema appears as appropriate.
Environment (please complete the following information):
- OS: Mac OS X
- Browser Firefox
- Version 108b
Additional context
Sanic version: 22.9.1
Python 3.10.8
sanic-routing==22.8.0, sanic-testing==22.9.0, sanic-ext==22.9.1
You can achieve what you want with this I believe:
class CategoryResponse:
data: Category
The problem is that we are trying to avoid nested class definitions like may be used in pydantic:
class Foo:
class Config:
...
Thanks! I was just looking through the tests to figure what are my options. :)
There is a PR #161 that solves the narrow case, but I am not sure it is the right solution.
At conflict are these two items:
class Foo:
class Ignore:
...
class Bar:
foo = Foo
We had been disallowing classes as properties for Pydantic support because of the nested Config
class. But, it would not cover this use case:
class Ignore:
...
class Foo:
ignore = Ignore
This seems like it would otherwise be legit pattern for Pydantic users:
class MyConfig:
extra = "allow"
class Foo(BaseModel):
bar: int
Config = MyConfig
I am wondering if perhaps we need an opt-in. Something like this:
```python
@openapi.include
class Foo:
class Ignore:
...
class Bar:
foo = Foo
@alg How do you feel about the opt-in pattern?
@ahopkins I'm not too deep into the way sanic-ext
things are done yet, so I'm not best suggester. I am actually fine with the version you pointed first. The version we used (Data = Category
) worked in sanic_openapi
and that's why I was asking. As long as we know how to transition, it's all good.
There are some competing goals. (1) I absolutely want there to be an easy migration pattern (meaning backwards compat), and (2) easy to implement (meaning compat with other tools and not forcing developers into a narrow pattern).
data: Category
is certainly the way to go as it will certainly work. sanic-openapi
was very opinionated and wanted you to use it's specific fields. This is not ideal and I really would like to get away from that.
I think that PR might sit on hold until next year so that we can think this one over. I want to leave this issue open because it does touch on something that needs solving.
I am using pydantic models in my sanic application. Ability to use nested Pydantic models within OpenAPI so it can include properties of an embedded object will be awesome.
My Pydantic model looks something like this-
class Foo(BaseModel):
data: Bar #where Bar is in turn a user defined pydantic model.
OpenAPI shows Bar
just as object and do not include it's properties.
I don't want to use sanic_openapi because of the same reason @ahopkins mentioned that it is highly opinionated and I probably won't be able to use pydantic with it.
Just putting this comment here to emphasize on need for this enhancement.
@iamvishalkhare That should work. You should be able to use Pydantic no problem. The issue here is using the old school sanic-openapi property types that had its own modelling system for creating definitions.
If you have a more complete example of something that is not working, LMK.
@ahopkins - As I wrote last comment, I realised what I was doing wrong. Also, I was driven by this discussion to believe this is not possible. You are right. This is no problem. I got this working. Thank you.
I got this working.
@iamvishalkhare Possibly of interest to you might be a new feature in 22.12.
@openapi.definition(
body={"application/json": openapi.Component(SomePydanticModel)}
)
That will automatically build out the schema using Pydantic's schema
method and inject them into the OAS components to make your doc more DRY.
openapi.Component(SomePydanticModel)
is the solution i searched for, but it's not in the docs. You might want to add this to the docs :D