drf-spectacular: Auto schema generation doesn't support pydantic schema
Describe the bug
I currently use SQLModel and Pydantic to support queries to my database and serialization of said data into json. Pydantic comes with a handy schema feature for all BaseModels that generates either a json or dict schema.
When I use a single model with no nested fields and pass in the Model’s dict schema to the @extend_schema decorator as one o the response params, ex: responses={200: my_model.schema()}, this works fine. However, if I have a model with another model as one of its fields (see here for an example), the schema generated from that isn’t interpreted correctly by drf-spectacular.
Specifically, the problem seems to be that Pydantic generates its documentation for nested models inside of a definitions key. I’m wondering if support could be added to drf-spectacular to move anything declared under the definitions keyword to the components/schema section of the api docs. Pydantic does add support for customizing the $ref location/prefix for models referenced elsewhere, so this wouldn’t be an issue.
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 29 (29 by maintainers)
Commits related to this issue
- Prevent exception for non-serializer classes targeted by SerializerExtensions #1006 In rare cases, extensions may target non-std classes like Pydantic classes. Changed lines cannot be circumvented by... — committed to tfranzel/drf-spectacular by tfranzel a year ago
- robustify subclass check in extensions #1006 — committed to tfranzel/drf-spectacular by tfranzel a year ago
- selectively distinguish real serializers from mocked ones #1006 This addresses an issue where we enter a code path that only works for real serializers, while the check also allows for objects covere... — committed to tfranzel/drf-spectacular by tfranzel a year ago
- selectively distinguish real serializers from mocked ones #1006 This addresses an issue where we enter a code path that only works for real serializers, while the check also allows for objects covere... — committed to tfranzel/drf-spectacular by tfranzel a year ago
- Merge pull request #1011 from tfranzel/smart_list_serializer_check selectively distinguish real serializers from mocked ones #1006 — committed to tfranzel/drf-spectacular by tfranzel a year ago
@tfranzel Excellent! With the new master version, it seems to work well!
I did these changes in my project to generate doc correctly:
Copied the master version of the extension to
myapp/schema.py.Thanks!
I see, they are very similar and we are dealing with them almost interchangeably.
At least we got it working in principle. Those minors are the reason we have no official support. The fact that the pydantic models are not based on serializers is kind of a monkey wrench in the gears.
cheers!
@caarmen, beware that you might want to add the (default on) enum processing back into the mix:
I will try the serializer extension route for completeness when I have some spare time and report results here.
I moved my models into an isolated module. I can now use the
APPEND_COMPONENTSas mentioned in https://github.com/tfranzel/drf-spectacular/issues/1006#issuecomment-1597273154 👍🏻However, using the extension instead of
APPEND_COMPONENTSresults in the original behavior raised in this issue.I’ll see if I can debug this and find anything further.
I added prints to
get_name()andmap_serializer()and they don’t appear.awesome @sydney-runkle, glad this works now. 🎉
@caarmen I think once you fix your app loading it should work for you too.
I attempted to do the serializer extension. Turns out Pydantic has some specifics we do not yet account for. So in the spirit of “eat your own dog food”, I fixed 2 minors to allow for proper functionality of this extension:
https://github.com/tfranzel/drf-spectacular/pull/1009/files#diff-5f1057f5cbe9827ecd29bac35d534f40e4beb52a49f53206bae7ad82bc4f2e4d
I added the extension to the blueprint section. Not sure yet whether we should support this officially, but people can at least copy&paste that extension for the time being.
Feedback would be much appreciated because I hastily put this together.
@sydney-runkle Thanks!
I think my issues are that my pydantic model classes are defined in a module that tries to access some drf code that requires the app being initialized. I assume I could refactor this and move those classes to an isolated module. I didn’t try it yet though, but I’ll be sure to come back here and check out this snippet if I do get a chance. Thanks again 😃
Sure thing! Here’s what I used for the
APPEND_COMMENTSroute:I’ve got tons of
pydanticmodels I need to include the schema for in the openapi docs, so I plan to create a module that abstracts away thepydantic.schema(...)logic so that I can keep thesettings.pyfile clean.Let me know if you have any additional questions! Nice to work with you all on this.
Funny there are a couple of us with the same need on the same day 😅
I tried the postprocessing hook and it seems to work 💪🏻
This example is explicitly handling specific models, it’s not generic.
Pydantic models (from link above):
View: Define a response of the outer model type
Model:post processing function in
com.myexamplemodule: Add the inner modelFooto thedefinitions:settings: Declare the post processing hook
https://github.com/tfranzel/drf-spectacular/blob/master/drf_spectacular/hooks.py
the second one is pretty basic. please read the above doc section carefully.
I think it might also be possible to write a serializer extension to accommodate this. I’ll throw a quick test together later but will most certainly not include it officially, which is okay as you can write/use extensions locally without issue.
I see. We accounted for raw schemas on a best effort basis but never really paid attention to this particular case.
I see 2 options (minus a big refactoring, which I am not that keen on):
write a postprocessing hook that cleans up, i.e. removing the components from the path and adding them in the components section and inserting a ref instead. https://drf-spectacular.readthedocs.io/en/latest/customization.html#step-6-postprocessing-hooks
only decorate a raw schema with a reference:
{"$ref": "#/components/schemas/XXX"}and either use a postprocessing hook or the settingAPPEND_COMPONENTSto fill in the definition part there withmy_model.schema().I think you can accomplish this with 1-2 helper functions and a bit of tooling. Keep in mind that OpenAPI ❤️.1 and JSONSchema are not 100% compatible. Usually not an issue, but your mileage may vary