Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return Pydantic model using Field aliases instead of names in FastAPI?

My FastAPI call is not returning the data in correct Response model format. It is returning data in database model format.

My database model:

class cat(DBConnect.Base):
     __tablename__ = 'category'
     __table_args__ = {"schema": SCHEMA}
     cat_id = Column('cat_id',UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
     cat_desc = Column('cat_desc', TEXT, nullable=True)
     cat_h__l_name = Column('cat_h_l_name', TEXT, nullable=True)

My Pydantic Model:

class CamelModel(BaseModel):
    class config:
        alias_generator = to_camel
        allow_population_by_field_name = True

class cat(CamelModel):
    cat_id =Field(alais='CatID', readonly=True)
    cat_description =Field(alias='CatDescription')
    cat_h__l_name = Field(alias='CatName')
     
    class config:
       orm_mode= True

My API call:

@router.patch('/cat/{id}/', response_model = 'cat')
def update_cat(response= Response, params: updatecat = Depends(updatecat)):
    response_obj = { resonse_code: status.HTTP_200_OK, 
    response_obj : {}    
    }
    
    response_obj = session.query() # It is returning the correct data from the database
    response.status_code = response_obj['response_code']
    
    return JSONResponse(response_obj['response_obj'], status_code = response_obj['response_code'])

Getting Response in below format:

     cat_id = 'some uuid'
     cat_desc = 'desc'
     cat_h__l_name = 'some h_l_name'

but I want the response to be in the below format:

CatID = 'some uuid'
CatDescription ='' some description'
CatName = 'Some cat name'

This code is not giving any errors (I have typed it, so might be some indentation or spelling mistake). The only issue is that the API doesn't return data in correct format. I have been stuck on it for a while.

like image 336
user15772986 Avatar asked Sep 03 '25 06:09

user15772986


1 Answers

To return a Pydantic model from an API endpoint using the Field aliases instead of names, you could add response_model_by_alias=True to the endpoint's decorator. This is mentioned in the documentation. Using response_model_by_alias=False would have the opposite effect. The example below uses the Model's Config alias_generator to automatically generate aliases for the fields.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


def to_pascal(string: str) -> str:
    return ''.join(word.capitalize() for word in string.split('_'))

class Item(BaseModel):
    name: str
    language_code: str
    
    class Config:
        alias_generator = to_pascal
        allow_population_by_field_name = True    
            

fake_db = [
    Item(Name='foo', LanguageCode='en'),
    Item(Name='bar', LanguageCode='fr')
]


@app.get('/item/{item_id}', response_model=Item, response_model_by_alias=True)
def create_item(item_id: int):
    return fake_db[item_id]

In Pydantic V2, the alias_generator could be used in a ConfigDict class, as shown in the documentation (along with response_model_by_alias=True). Pydantic offers three built-in alias generators: to_pascal, to_camel, and to_snake. Also, in v2.0, the allow_population_by_field_name configuration setting was changed to populate_by_name. This setting indicates whether an aliased field may be populated by its name as given by the model attribute, as well as the alias. Defaults to False.

from pydantic import BaseModel, ConfigDict
from pydantic.alias_generators import to_pascal

class Item(BaseModel):
    model_config = ConfigDict(alias_generator=to_pascal, populate_by_name=True)
    name: str
    language_code: str

If one would like to create a Field alias without using an alias_generator, they could achieve this as follows:

from pydantic import BaseModel, ConfigDict, Field

class Item(BaseModel):
    model_config = ConfigDict(populate_by_name=True)
    name: str = Field(..., alias='Name')
    language_code: str = Field(..., alias='LanguageCode')

In case you used an alias_generator, as well as specified an alias on the Field, the alias will take precedence over the generated alias by default.

Also, as explained in Pydantic's documentation:

The alias parameter is used for both validation and serialization. If you want to use different aliases for validation and serialization respectively, you can use the validation_alias and serialization_alias parameters, which will apply only in their respective use cases.

In case you use alias together with validation_alias or serialization_alias at the same time, the validation_alias will have priority over alias for validation, and serialization_alias will have priority over alias for serialization.

You may also set alias_priority on a field to change this behavior.

Returning a Pydantic model using Field aliases in a JSONResponse

It should also be noted that you seem to be returning a JSONResponse directly, instead of a Pydantic model from your API endpoint (which would be later wrapped into a JSONResponse by FastAPI, behind the scenes, and returned to the client); hence, setting response_model_by_alias=True in the endpoint's decorator would have no effect. Thus, in that case, you could convert the model to a dictionary, using Pydantic's model.dict(...) (Note: in Pydantic V2, this method has been replaced by model.model_dump(...)), and set the by_alias argument to True.

On a side note, you cannot put a Pydantic model in a JSONResponse without first converting it to a dict, if you have data types, such as datetime, UUID, etc., that cannot be serialized. For those cases, you can use the jsonable_encoder to convert your data before passing it to a response. The jsonable_encoder will ensure that objects that are not serializable will be converted to a str. Have a look at this answer for more details.

from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder

@app.get('/item/{item_id}')
def create_item(item_id: int):
    return JSONResponse(jsonable_encoder(fake_db[item_id].model_dump(by_alias=True)), status_code=200)
like image 69
Chris Avatar answered Sep 04 '25 21:09

Chris