fastapi icon indicating copy to clipboard operation
fastapi copied to clipboard

fix: return dict response model default factory is not work

Open vvanglro opened this issue 1 year ago • 3 comments

@tiangolo Hi, When returning a dictionary, if there is a default_factory in the model, it will not take effect, and the return value is None. Normal if returned as a model.

The default_factory added by pydantic in version 1.5, I checked the documentation and it seems that it is still in the beta stage, so is this the reason does not support default_factory or is there another reason? But I think the consistency of the two return methods should be kept.

Related Documentation Links: https://docs.pydantic.dev/latest/usage/models/#field-with-dynamic-default-value https://github.com/pydantic/pydantic/issues/866

Related Issues: https://github.com/tiangolo/fastapi/discussions/8394 https://github.com/tiangolo/fastapi/discussions/8853

Here is a minimal reproducible example:

from functools import partial
from typing import Optional

import fastapi
from fastapi import Request
from pydantic import BaseModel, Field
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.responses import Response

app = fastapi.FastAPI()


TRANSLATIONS = {
    "zh_CN": {"操作成功": "操作成功"},
    "en_US": {"操作成功": "Successful operation."},
}

translations = TRANSLATIONS.get("zh_CN")


def set_locale(locale: str):
    global translations
    translations = TRANSLATIONS.get(locale) or TRANSLATIONS.get("zh_CN")


def _(msg: str):
    return translations.get(msg)


class InternationalizationMiddleware(BaseHTTPMiddleware):

    async def dispatch(
            self, request: Request, call_next: RequestResponseEndpoint
    ) -> Response:
        lang_code: Optional[str] = request.headers.get("Accept-Language", None)
        set_locale(lang_code)
        response: _StreamingResponse = await call_next(request)  # type: ignore
        return response


app.add_middleware(InternationalizationMiddleware)


class ResponseModel(BaseModel):
    code: int = 200
    message: str = Field(default_factory=partial(_, msg="操作成功"))


@app.get('/not_work', response_model=ResponseModel)
async def func():
    return {"code": 200}


@app.get('/work', response_model=ResponseModel)
async def func():
    return ResponseModel()

if __name__ == '__main__':
    import uvicorn

    uvicorn.run(app, port=8089)

vvanglro avatar Jun 20 '23 06:06 vvanglro