-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
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
Fix unskipped defaults #422
Conversation
Codecov Report
@@ Coverage Diff @@
## master #422 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 240 241 +1
Lines 5626 5642 +16
=====================================
+ Hits 5626 5642 +16
Continue to review full report at Codecov.
|
@@ -52,6 +52,8 @@ def serialize_response( | |||
errors.extend(errors_) | |||
if errors: | |||
raise ValidationError(errors) | |||
if skip_defaults and isinstance(response, BaseModel): | |||
exclude |= value.__fields_set__.difference(response.__fields_set__) | |||
return jsonable_encoder( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if isinstance(response, BaseModel):
value = response.dict(skip_defaults=skip_defaults)
might also work, but I'm not sure if that would play differently with include/exclude/jsonable_encoder
. And given skip_defaults
is probably usually false, I think the version above will be more performant in most cases.
44546a4
to
c39a5c8
Compare
Wow, you had to go quite deep into the rabbit hole to find that ( I updated the test to check for defaults in sub-models. And updated the implementation to use And yeah, it's unfortunate to have to do a second parse of the models and have to "clone" models. That came after adding Pydantic ORM mode, as now the data is serialized by Pydantic directly (including DB models). And a sub-model including something sensitive (e.g. a To avoid that security issue, I had to "clone" the models declared in |
Signed-off-by lmignon
Addresses #421
Currently, if you return a
BaseModel
instance in a route with aBaseModel
response_type, theskip_defaults
value does not influence the result. This PR fixes this.Details
The source of the problem is that calling
field.validate
on a model results in a new model with the fields set. I dug in on the pydantic side and it looks like this is due to the recent change to re-parse the model with a non-subclass type when response_model is set. If you look inBaseModel.validate
(which is ultimately called if you follow it deep enough), you can see the precise spot where defaults aren't being skipped:It calls into
parse_obj
on the last line, which subsequently callsdict
on the object and then parses the dict. Unfortunately, this means defaults don't get skipped.I don't think it makes sense to change this on the pydantic side because otherwise you'd run into problems parsing models with default values into non-subclass models with required values. So, if we want to stick with this approach of creating a new class to force a reparse, we probably need to handle the
That said, I would also be in favor of a larger refactor if we could come up with a way to get the desired field safely without another round of parsing/dumping 😬. But that does seem hard.