Skip to content

✨ Support for mixed and multiple Pydantic models for parameters using Query, Cookie and Header#12481

Open
JSCU-CNI wants to merge 8 commits intofastapi:masterfrom
JSCU-CNI:bug/flat-fields-for-all-models
Open

✨ Support for mixed and multiple Pydantic models for parameters using Query, Cookie and Header#12481
JSCU-CNI wants to merge 8 commits intofastapi:masterfrom
JSCU-CNI:bug/flat-fields-for-all-models

Conversation

@JSCU-CNI
Copy link
Copy Markdown

From #12199 onwards, Pydantic models are supported for query, cookie and header parameters. When one parameter is present, the model is flattened in the OpenAPI spec, but when multiple are defined, they aren't.

This is confusing, and results in a confusing OpenAPI spec. Since these arguments are used in flattened form anyway, it makes more sense to flatten all of them.

@svlandeg svlandeg added the feature New feature or request label Oct 25, 2024
@svlandeg
Copy link
Copy Markdown
Member

Thanks for the PR! As you consider this to be a bug, can you commit a unit test that shows the behaviour of this functionality on this PR, which would break on master?

@JSCU-CNI JSCU-CNI force-pushed the bug/flat-fields-for-all-models branch from e851c68 to 792ee21 Compare November 5, 2024 17:00
@JSCU-CNI
Copy link
Copy Markdown
Author

JSCU-CNI commented Nov 5, 2024

@svlandeg I've updated the PR with more tests, and fixed something uncovered by those tests 👍

@github-actions github-actions Bot removed the waiting label Nov 5, 2024
@ollie-bell
Copy link
Copy Markdown

Looking forward to this, it fixes the issue described in this comment #12199 (comment)

@JSCU-CNI JSCU-CNI changed the title 🐛 Flatten multiple Pydantic models for parameters in path, query, header and cookie ✨ Support for mixed and multiple Pydantic models for parameters using Query, Cookie and Header Nov 7, 2024
@JSCU-CNI
Copy link
Copy Markdown
Author

JSCU-CNI commented Nov 7, 2024

Reflecting on this, I believe we could classify this as a feature rather than a bug. For the first test case, i.e. async def with_depends(model1: Model = Query(), dependency=Depends(dependency)):, this PR may constitute a bug fix (as it handled properly), but now that everything is properly flattened, this would constitute a new (and rather useful) feature.

It now properly handles multiple Pydantic models, dependencies and direct arguments. I've updated the PR title to reflect this.

@Tomtommytomtom
Copy link
Copy Markdown

Would love to see this merged, the query param models are mostly unusable for us in their current state

@thraizz
Copy link
Copy Markdown

thraizz commented Nov 27, 2024

Also waiting for this to be merged 😺

@JSCU-CNI
Copy link
Copy Markdown
Author

@svlandeg Is there anything we can do to move this PR forward?

@svlandeg
Copy link
Copy Markdown
Member

svlandeg commented Dec 2, 2024

Hi! We really appreciate the time and effort spent to submit this PR. We have a high volume of PRs and we're working hard to review and classify them. We'll get back to you once we've been able to review this in detail - thanks for your patience! 🙏

@Abdeldjalil-H
Copy link
Copy Markdown

Hi everyone! Any updates on this PR?

@svlandeg svlandeg self-assigned this Feb 3, 2025
@JSCU-CNI JSCU-CNI force-pushed the bug/flat-fields-for-all-models branch from 792ee21 to 86d9bb1 Compare March 25, 2025 10:22
@JSCU-CNI
Copy link
Copy Markdown
Author

A conflict with PR #13515 has been resolved, and this PR should be able to be merged directly.

We would love to move forward with this PR, as there appears to be clear support for this feature. If there's anything we can do to help, please let us know @svlandeg

@YuriiMotov
Copy link
Copy Markdown
Member

@JSCU-CNI, thank you for your work!

I haven't delved into the implementation details yet, just looked at it briefly.

As I can see, the case with BaseModel that has extra="forbid" is not handled, right?
If we have model with extra="forbid" and add more single parameters or another model, the validation of the model will fail and the error message might be not clear to understand what is happening.
I think we should handle this and show warning (or even error) on startup if such model (with extra="forbid" is combined with another model or single parameters).
What do you think?

One more thing to think about: if we have 2 models and both of them have field with the same name, will this be handled well? Intuitively, everything should be Ok, but better check it and probably add test to make it explicit.

@JSCU-CNI JSCU-CNI force-pushed the bug/flat-fields-for-all-models branch 6 times, most recently from fb793e7 to 39241de Compare July 11, 2025 09:51
@JSCU-CNI
Copy link
Copy Markdown
Author

Thank you for your comment @YuriiMotov. The implementation closely follows the original code, and only makes some necessary adjustments. It's no coincidence that PR #12944 closely resembles this code, as they probably did a similar thing.

Regarding your comment on extra=forbid, it is hard to detect whether the use-case is valid, as all models may share fully overlapping arguments. In these cases, everything will function as intended, and a warning should not be emitted. Only when models are not fully overlapping, this should trigger. I've at least added a couple of tests to cover this. I'm on the fence on whether this PR should address this more elaborately rather than leaving if unaddressed, as extra=forbid is arguably a niche edge case regardless. We could probably solve this by stating this fact in the documentation.

You're correct that everything functions properly when there are overlapping field names in different models, but I've added several test cases to verify this as well.

I've rebased and squashed all commits, and added some comments to the test cases to explain them a bit better. I've also added a test where I mix various models, just to fully cover our bases.

@YuriiMotov
Copy link
Copy Markdown
Member

@JSCU-CNI, thanks for the explanation!
I think I agree about extra=forbid. Actually it's not that hard to find the cause of "Extra inputs are not permitted" error.

One more thing. There are no tests for convert_underscores. And it seems that it doesn't work correctly:

class Model3(BaseModel):
    field_1: str

class Model4(BaseModel):
    field_2: str

@app.get("/headers2")
async def get_hhh(
    h1: Annotated[Model3, Header(convert_underscores=False)],
    h2: Annotated[Model4, Header(convert_underscores=False)],
):
    return {"h1": h1, "h2": h2}

Shows parameters as param-1 and param-2 in schema.

@JSCU-CNI JSCU-CNI force-pushed the bug/flat-fields-for-all-models branch 2 times, most recently from 23b5d4c to d65888d Compare July 14, 2025 15:35
@bjmc
Copy link
Copy Markdown

bjmc commented Nov 21, 2025

Could this solution be extended to cover Form() parameters also? We're building an app using HTMX that is intended to degrade gracefully in situations without Javascript (so no AJAX, no JSON) and it would be extremely convenient to POST one flat form and have the backend automatically generate separate Pydantic models for us.

@nox-cadmatic
Copy link
Copy Markdown

Looking forward to this being merged. I expected this to work as described going off of the documentation. Thanks for the work to all involved.

@github-actions github-actions Bot added the conflicts Automatically generated when a PR has a merge conflict label Dec 12, 2025
@github-actions

This comment was marked as resolved.

@arbroen
Copy link
Copy Markdown

arbroen commented Jan 5, 2026

Looking forward as well, this will allow us to combine filters with fastapi-pagination. Cheers

@svlandeg svlandeg self-assigned this Jan 5, 2026
@JSCU-CNI JSCU-CNI force-pushed the bug/flat-fields-for-all-models branch from 00693fd to f799a56 Compare January 7, 2026 13:32
@github-actions github-actions Bot removed the conflicts Automatically generated when a PR has a merge conflict label Jan 7, 2026
@JSCU-CNI JSCU-CNI force-pushed the bug/flat-fields-for-all-models branch from f799a56 to 32a5220 Compare January 7, 2026 13:34
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Jan 7, 2026

Merging this PR will not alter performance

✅ 20 untouched benchmarks


Comparing JSCU-CNI:bug/flat-fields-for-all-models (959f944) with master (840e462)1

Open in CodSpeed

Footnotes

  1. No successful run was found on master (c38782e) during the generation of this report, so 840e462 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@JSCU-CNI
Copy link
Copy Markdown
Author

JSCU-CNI commented Jan 7, 2026

Resolved merge conflicts caused by #14371, #14564 and #14609.

…g Query, Cookie and Header

From fastapi#12199 onwards, Pydantic models are supported for query, cookie and
header parameters. When one parameter is present, the model is flattened
in the OpenAPI spec, but when multiple are defined, they aren't.

This is confusing, and results in a confusing OpenAPI spec. Since these
arguments are used in flattened form anyway, it makes more sense to
flatten all of them.
@JSCU-CNI JSCU-CNI force-pushed the bug/flat-fields-for-all-models branch from 32a5220 to 6aa2546 Compare January 7, 2026 13:57
@stephaneglaugier91
Copy link
Copy Markdown

This change is crucial for our team, many thanks to all those involved! 🙏

It's difficult to take full advantage of Depends with Query parameters without this feature.

Copy link
Copy Markdown
Member

@svlandeg svlandeg left a comment

Choose a reason for hiding this comment

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

The implementation here looks clean, and I agree that flattening multiple Pydantic models alongside eachother feels like the most intuitive thing, considering how this works with a single model.

This is a breaking change though - old code using either one Pydantic model + additional atomic parameters, or multiple Pydantic models, will now behave differently. Maybe @YuriiMotov 's idea of introducing a flatten= parameter would help users preserve existing functionality in case they'd want it?

With respect to the docs: I don't see any pages that require updating. But we should definitely include some kind of warning in the release notes. And maybe add a docs example of using multiple models, and explain how they'd be flattened? (this can be added in a follow-up PR)

@JSCU-CNI : thank you for the time and effort you've already put into this PR! Appreciate the responsiveness and clear communication. I also think this PR has gotten a lot stronger already from you adressing @YuriiMotov's comments 🙏

@svlandeg svlandeg removed their assignment Feb 4, 2026
@svlandeg
Copy link
Copy Markdown
Member

svlandeg commented Feb 4, 2026

It would be great if Tiangolo can have a final look at this before we merge, I've solicited his feedback 🙏

@github-actions github-actions Bot added the conflicts Automatically generated when a PR has a merge conflict label Feb 7, 2026
@github-actions

This comment was marked as resolved.

@github-actions github-actions Bot removed the conflicts Automatically generated when a PR has a merge conflict label Feb 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.