Skip to content

✨ Don't revalidate the response content if it is of the same type as response_model#4416

Closed
kaiix wants to merge 4 commits into
fastapi:masterfrom
kaiix:response-model-revalidate
Closed

✨ Don't revalidate the response content if it is of the same type as response_model#4416
kaiix wants to merge 4 commits into
fastapi:masterfrom
kaiix:response-model-revalidate

Conversation

@kaiix

@kaiix kaiix commented Jan 12, 2022

Copy link
Copy Markdown

@kaiix kaiix force-pushed the response-model-revalidate branch from 5489df0 to 28c24dc Compare January 13, 2022 03:00
Comment thread fastapi/routing.py Outdated
Comment thread fastapi/routing.py
@codecov

This comment was marked as outdated.

@github-actions

This comment was marked as outdated.

@github-actions

Copy link
Copy Markdown
Contributor

📝 Docs preview for commit a9b3e8c at: https://639cd42098600317dc000812--fastapi.netlify.app

@dolfinus

dolfinus commented Mar 5, 2023

Copy link
Copy Markdown
Contributor

@tiangolo any chance this PR will be reviewed?

@Tishka17

Tishka17 commented Mar 5, 2023

Copy link
Copy Markdown

Rebuilding a response model is a real problem and cannot actually be done correctly because of aliases and other settings (I guess I've already left a comment about this). In normal code your create it by your own from the other sources that could not be converted automatically so it is actually not needed.

Can we expect this to be reviewed and fixed?

@nkhitrov

Copy link
Copy Markdown

@tiangolo hi! Can you answer on this comment?

#4416 (comment)

@levsh

levsh commented Jun 6, 2023

Copy link
Copy Markdown

Hi!
I have this problem too (
This can greatly degrade performance for the complex models with validators or endpoints that return a large number of items.
In example below get_items endpoint already returns a list of items so it is no need to rebuild response again.
And finally we have response in ~2 second instead of ~1 second.

from typing import List
from uuid import UUID, uuid4

from fastapi import FastAPI
from pydantic import BaseModel, validator

app = FastAPI()

class Data(BaseModel):
    i: int

    @validator("i")
    def validate_i(cls, v):
        sleep(0.1)
        return v

class Item(BaseModel):
    id: UUID
    data: Data

@app.get(
    "/items",
    response_model=List[Item],
)
def get_items():
    return [Item(id=uuid4(), data=Data(i=i)) for i in range(10)]

Thank you!

@Tishka17

Tishka17 commented Jul 6, 2023

Copy link
Copy Markdown

@tiangolo are there any plans on merging or reimplementing this?

@Danipulok

Copy link
Copy Markdown

@tiangolo is there any way this PR could be merged?

@tiangolo tiangolo added feature New feature or request p4 and removed investigate labels Jan 15, 2024
@patrick91 patrick91 changed the title Don't revalidate the response content if it is of the same type as response_model ✨ Don't revalidate the response content if it is of the same type as response_model Aug 9, 2024

@patrick91 patrick91 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me, I rebased it and update the tests for Pydantic 2 :)

Comment thread tests/test_serialize_response_model.py Outdated
@kaiix kaiix force-pushed the response-model-revalidate branch from d2946c3 to b582069 Compare February 28, 2025 16:04
@kaiix

kaiix commented Feb 28, 2025

Copy link
Copy Markdown
Author

Updated codes and tests.

When users are using Pydantic v2 (fortunately, most applications should be using v2 by now), this issue doesn’t need much attention.

In v2, field.validate will call type_adapter.validator. If you check its schema, you’ll see that the validator will not revalidate instances of response_model (see reference: revalidate_instances).

[This is part of the validator schema for the AutoIncrement object in the test case]

 SchemaValidator(title="AutoIncrement", validator=Model(
    ModelValidator {
        revalidate: Never,
        validator: ModelFields(
            ModelFieldsValidator {
                fields: [
                    Field {
                        name: "count",
                        lookup_key: Simple {
                            key: "count",
...

Although there are some additional function calls, it won’t trigger the validator in a way that would modify the original response_content value in certain cases.

Therefore, this fix is only relevant for applications that are still using Pydantic v1.

@kaiix kaiix force-pushed the response-model-revalidate branch from b582069 to b5fcef9 Compare March 3, 2025 13:14
@kaiix kaiix force-pushed the response-model-revalidate branch from b5fcef9 to ec08e23 Compare March 3, 2025 13:31
def test_response_model_should_not_revalidate_response_content_if_they_had_same_type():
response = client.post("/increment")
response.raise_for_status()
assert response.json() == {"count": 1}

@svlandeg svlandeg Sep 12, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just to confirm: on master and with Pydantic 1.10.22, this fails with

AssertionError: assert {'count': 2} == {'count': 1}

@github-actions github-actions Bot added the conflicts Automatically generated when a PR has a merge conflict label Sep 29, 2025
@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 Sep 29, 2025
@svlandeg svlandeg marked this pull request as draft September 29, 2025 08:36
@svlandeg

svlandeg commented Sep 29, 2025

Copy link
Copy Markdown
Member

The changes from #14099 are interfering with this PR... 🕵️‍♀️

[UPDATE]: solved ✔️

@svlandeg svlandeg marked this pull request as ready for review September 29, 2025 09:18
@github-actions github-actions Bot added the conflicts Automatically generated when a PR has a merge conflict label Dec 27, 2025
@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 Jan 28, 2026
@codspeed-hq

codspeed-hq Bot commented Jan 28, 2026

Copy link
Copy Markdown

CodSpeed Performance Report

Merging this PR will degrade performance by 94.53%

Comparing kaiix:response-model-revalidate (ba95d1f) with master (8c32e91)

Summary

⚡ 2 improved benchmarks
❌ 2 regressed benchmarks
✅ 16 untouched benchmarks

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
test_sync_return_large_model_with_response_model 13.4 ms 218 ms -93.86%
test_sync_return_model_with_response_model 6.2 ms 5.6 ms +11.25%
test_async_return_large_model_with_response_model 11.9 ms 217 ms -94.53%
test_sync_receiving_validated_pydantic_model 6.5 ms 5.8 ms +10.87%

@svlandeg svlandeg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We've recently dropped support for Pydantic v1, so I'm afraid this PR is no longer really useful. @kaiix, thanks again for all the work you've put into this and I'm sorry we weren't able to merge it sooner 🙏

@svlandeg svlandeg closed this Jan 28, 2026
@creative-being

This comment was marked as spam.

5 similar comments
@creative-being

This comment was marked as spam.

@creative-being

This comment was marked as spam.

@creative-being

This comment was marked as spam.

@creative-being

This comment was marked as spam.

@creative-being

This comment was marked as spam.

@fastapi fastapi locked as spam and limited conversation to collaborators Feb 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

feature New feature or request p4

Projects

None yet

Development

Successfully merging this pull request may close these issues.