zhanymkanov / fastapi-best-practices

FastAPI Best Practices and Conventions we used at our startup
9.4k stars 701 forks source link

Pydantic base model to parse and return camelCase JSON #17

Open IrrerPolterer opened 1 year ago

IrrerPolterer commented 1 year ago

Credit: This idea has been inspired by this article and and this comment.

I want to be able to both...

For this I'm using a custom base model for pretty much all my Pydantic schemas:

from humps import camelize
from pydantic import BaseModel

class BaseSchema(BaseModel):
    class Config:
        # enable sqlalchemy model parsing
        orm_mode = True

        # enable camelCase JSON parsing
        alias_generator = camelize
        allow_population_by_field_name = True

    # enable camelCase json response
    def json(self, *args, **kwargs):
        kwargs.setdefault("by_alias", True)
        return super().json(*args, **kwargs)

(This requires the pyHumps package.)


With this BaseSchema, I can now create pydantic schemas with the intended behavior:

class Device(BaseSchema):
    name: str | None
    serial_number: str

# ingest camelCase
device = Device.parse_raw("""{"name": "Device XYZ", "serialNumber": "XYZ-123-ABC-000"}""")

# return camelCase
print(device.json())
# {"name": "Device XYZ", "serialNumber": "XYZ-123-ABC-000"}

My solution above uses the camelize function from the pyhumps package. Alternatively, you can create the function yourself like this:

def camelize(string: str) -> str:
    string_split = string.split("_")
    return string_split[0] + "".join(word.capitalize() for word in string_split[1:])
zhanymkanov commented 1 year ago

Hey,

That's the good one! We do the same within our projects 💯

I didn't include it in the list since not every project needs camelCase, but this issue could be a good reference for those who do 👍

copdips commented 7 months ago

the new version of Pydantic includes all of these built-in, no need to install humps anymore:

from pydantic import BaseModel
from pydantic.alias_generators import to_camel

class BaseSchema(BaseModel):
    model_config = ConfigDict(
        alias_generator=to_camel,
        populate_by_name=True,
    )