Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Comments

Fix unskipped defaults#422

Merged
tiangolo merged 3 commits intofastapi:masterfrom
dmontagu:model-skip-defaults
Aug 26, 2019
Merged

Fix unskipped defaults#422
tiangolo merged 3 commits intofastapi:masterfrom
dmontagu:model-skip-defaults

Conversation

@dmontagu
Copy link
Collaborator

@dmontagudmontagu commentedAug 6, 2019
edited
Loading

Addresses#421

Currently, if you return aBaseModel 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 callingfield.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:

@classmethoddefvalidate(cls:Type['Model'],value:Any)->'Model':ifisinstance(value,dict):returncls.parse_obj(value)elifisinstance(value,cls):returnvalue.copy()elifcls.__config__.orm_mode:returncls.from_orm(value)else:withchange_exception(DictError,TypeError,ValueError):returncls.parse_obj(value)

It calls intoparse_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.

koxudaxi and wozniakty reacted with thumbs up emoji
@codecov
Copy link

codecovbot commentedAug 6, 2019
edited
Loading

Codecov Report

Merging#422 intomaster willnot change coverage.
The diff coverage is100%.

Impacted file tree graph

@@          Coverage Diff          @@##           master   #422   +/-   ##=====================================  Coverage     100%   100%           =====================================  Files         240    241    +1       Lines        5626   5642   +16     =====================================+ Hits         5626   5642   +16
Impacted FilesCoverage Δ
fastapi/routing.py100% <100%> (ø)⬆️
tests/test_skip_defaults.py100% <100%> (ø)

Continue to review full report at Codecov.

Legend -Click here to learn more
Δ = absolute <relative> (impact),ø = not affected,? = missing data
Powered byCodecov. Last update033bc2a...0843c6b. Read thecomment docs.

Copy link
CollaboratorAuthor

@dmontagudmontaguAug 6, 2019
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

ifisinstance(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 givenskip_defaults is probably usually false, I think the version above will be more performant in most cases.

@tiangolotiangolo merged commit38495ff intofastapi:masterAug 26, 2019
@tiangolo
Copy link
Member

Wow, you had to go quite deep into the rabbit hole to find that (field.validate andBaseModel.validate are quite deep inside the callback flow in Pydantic). Good job! Thanks! 👏 🚀

I updated the test to check for defaults in sub-models. And updated the implementation to usevalue = response.dict(skip_defaults=skip_defaults) to take into account those sub-models.


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. ahashed_password) still passes validation as-is for the parent model. So, a modelclass UserWithPassword(BaseUser) passes validation for a modelclass BaseUser(BaseModel), including the extra sensitive data.

To avoid that security issue, I had to "clone" the models declared inresponse_model, to make sure they are a "different" class, and sub-classes don't pass directly but have to be parsed and validated.

dmontagu reacted with thumbs up emoji

lmignon pushed a commit to acsone/fastapi that referenced this pull requestSep 19, 2024
Signed-off-by lmignon
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

2 participants

@dmontagu@tiangolo

[8]ページ先頭

©2009-2026 Movatter.jp