question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Is it possible to apply alias generator from pydantic to FastApi fields?

See original GitHub issue

First Check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn’t find it.
  • I searched the FastAPI documentation, with the integrated search.
  • I already searched in Google “How to X in FastAPI” and didn’t find any information.
  • I already read and followed all the tutorial in the docs and didn’t find an answer.
  • I already checked if it is not related to FastAPI but to Pydantic.
  • I already checked if it is not related to FastAPI but to Swagger UI.
  • I already checked if it is not related to FastAPI but to ReDoc.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

from fastapi import Body, Query
from pydantic import BaseConfig, BaseModel, Field


def to_camelcase(string: str) -> str:
    res = ''.join(word.capitalize() for word in string.split('_'))
    return res[0].lower() + res[1:]


class User(BaseModel):
    class Config(BaseConfig):
        alias_generator = to_camelcase
        allow_population_by_field_name = True

    first_name: str = Field()
    last_name: str = Field()
    

@app.post('/user')
def create_user(
    user: User = Body(...), 
    send_invite: bool = Query(...),
):
    ...

Description

On backend we’re using FastApi and native to python snake_case notation, however, on frontend - there is a JS with their camelCase notation. In the code above, client can submit body with either snake or camel case and pydantic will do the magic to understand it. However, for the query parameter it doesn’t work this way: send_invite is expected to be in snake case only. So this would be a violation of the overall structure either on the UI (they’d have to use snake for query and camel for body) or in the api (snake for everything, but camel for the query).

Thus, i wonder, what would be the best way to work this around?

Operating System

Other

Operating System Details

Not applicable (doesn’t matter)

FastAPI Version

0.65.2

Python Version

3.9

Additional Context

Pydantic version: 1.8.2

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:11 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
tc-imbacommented, Nov 5, 2021

@mclate Any snippets or workarounds for alias_generator that we can use with Query or Field params?

I’ve studied on this for a while, there are several workarounds, but all not perfect. I’m going to write a pr to provide a perfect solution to it.

I’m going to share some of the workarounds here:

polyfill alias_generator

with a decorator

Since we also need to modify the generated schema, dependency is not useful here. I use a decorator to update the alias attribute of Query and Path. Maybe some more types of Param can be updated as well.

I use the makefun module to modify the signature of the function. I use the snake2camel from the fastapi_utils module, you can use any function to generate the alias you prefer.

from inspect import Parameter, signature
from makefun import wraps
from fastapi_utils.camelcase import snake2camel

def camelcase_parameters(func: Any) -> Any:
    func_sig = signature(func)
    parameters = list(func_sig.parameters.values())
    for i, parameter in enumerate(parameters):
        if (
            parameter.default
            and isinstance(parameter.default, (params.Query, params.Path))
            and parameter.default.alias is None
        ):
            parameter.default.alias = snake2camel(parameter.name, start_lower=True)

    new_sig = func_sig.replace(parameters=parameters)

    @wraps(func, new_sig=new_sig)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        if "camelcase_parameters_dependency" in kwargs:
            del kwargs["camelcase_parameters_dependency"]
        return func(*args, **kwargs)

    return wrapper

Add the camelcase_parameters decorator between the @router.xxx and the route function definition. Now all Query and Path without alias will have a generated one (the camalCase of the original name).

@router.get("/test")
@camelcase_parameters
async def test_route(invitation_code: str = Query(...)):
    return {"invitation_code": invitation_code}

monkey patch fastapi dependency

Another solution is to modify fastapi itself in fastapi/dependencies/utils.py. The functions in this file are mainly for parsing the dependencies. We can modify a function get_param_field.

On this line

https://github.com/tiangolo/fastapi/blob/58ab733f19846b4875c5b79bfb1f4d1cb7f4823f/fastapi/dependencies/utils.py#L381

We can find that alias is set from param.name if field_info.alias is not provided. If we change it into

if field_info.alias:
    alias = field_info.alias
else:
    alias = snake2camel(param.name, start_lower=True)

Now a generated alias with snake2camel is set.

The complete version of the patch is

from fastapi.dependencies.utils import *
from fastapi.dependencies import utils

def get_param_field(
    *,
    param: inspect.Parameter,
    param_name: str,
    default_field_info: Type[params.Param] = params.Param,
    force_type: Optional[params.ParamTypes] = None,
    ignore_default: bool = False,
) -> ModelField:
    default_value = Required
    had_schema = False
    if not param.default == param.empty and ignore_default is False:
        default_value = param.default
    if isinstance(default_value, FieldInfo):
        had_schema = True
        field_info = default_value
        default_value = field_info.default
        if (
            isinstance(field_info, params.Param)
            and getattr(field_info, "in_", None) is None
        ):
            field_info.in_ = default_field_info.in_
        if force_type:
            field_info.in_ = force_type  # type: ignore
    else:
        field_info = default_field_info(default_value)
    required = default_value == Required
    annotation: Any = Any
    if not param.annotation == param.empty:
        annotation = param.annotation
    annotation = get_annotation_from_field_info(annotation, field_info, param_name)
    if not field_info.alias and getattr(field_info, "convert_underscores", None):
        alias = param.name.replace("_", "-")
    else:
        # THE PATCH IS HERE
        if field_info.alias:
            alias = field_info.alias
        else:
            alias = snake2camel(param.name, start_lower=True)
    field = create_response_field(
        name=param.name,
        type_=annotation,
        default=None if required else default_value,
        alias=alias,
        required=required,
        field_info=field_info,
    )
    field.required = required
    if not had_schema and not is_scalar_field(field=field):
        field.field_info = params.Body(field_info.default)

    return field

utils.get_param_field = get_param_field

You should put these lines before importing your apis (or using any dependency). In my project, I put it the first line in the file with app = FastAPI(...).

However, this method is quite dirty. You have to fix to a fastapi release because if the function is modifed in a future release, it may introduce bad behaviors.

1reaction
havardthomcommented, Aug 26, 2021

I would love a FastAPI app wide alias_generator type functionality, to be applied to e.g. Query and Path parameters. Currently I am manually defining camelCase aliases for all Path and Query parameters in my app.

Read more comments on GitHub >

github_iconTop Results From Across the Web

return pydantic model with field names instead of alias as ...
I know it's somewhat possible when using the dict method of the model, but it doesn't feel right and messes up the typing...
Read more >
Body - Fields - FastAPI
You can use Pydantic's Field to declare extra validations and metadata for model attributes. You can also use the extra keyword arguments to...
Read more >
CamelCase Models with FastAPI and Pydantic - Medium
We can generate aliases for all of the model fields as follows: from pydantic import BaseModel from humps import camelize
Read more >
datamodel-code-generator - GitHub Pages
This code generator creates pydantic model from an openapi file and others. ... [--original-field-name-delimiter ORIGINAL_FIELD_NAME_DELIMITER] ...
Read more >
Settings management - pydantic
Create a clearly-defined, type-hinted application configuration class ... Note 2: Field aliases are ignored when building the environment variable name.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found