Skip to content

Latest commit

 

History

History
301 lines (244 loc) · 8.53 KB

migrating_versions.md

File metadata and controls

301 lines (244 loc) · 8.53 KB

FastAPI - Cruddy Framework: Migrating Versions

Logo

Migration Guide

fastapi-cruddy-framework 0.x.x -> 1.x.x

Dependency Guides:

Code Mods Required:

  1. Modify BaseSettings imports:
  • Any project files using pydantic's BaseSettings ENV config model must shift their import target to pydantic_settings
from pydantic import BaseSettings

becomes

from pydantic_settings import BaseSettings

  1. Replace @app.on_event("startup") and @app.on_event("shutdown") with lifespan context manager.
  • Upgrade your main.py file:
from fastapi import FastAPI
from myproject.router import application as application_router

app = FastAPI(title="My Project", version="0.0.0")


@app.on_event("startup")
async def bootstrap():
    app.include_router(application_router)


@app.on_event("shudtown")
async def shutdown():
    pass

becomes

from contextlib import asynccontextmanager
from fastapi import FastAPI
from myproject.router import application as application_router


async def bootstrap(application: FastAPI):
    application.include_router(application_router)


async def shutdown():
    pass


@asynccontextmanager
async def lifespan(application: FastAPI):
    await bootstrap(application)
    yield
    await shutdown()


app = FastAPI(
    title="My Project", version="0.0.0", lifespan=lifespan
)

  1. (Optional) As of December 2023, there is a known bug with sqlmodel 0.0.14 where type checking on TABLE models will complain, but will still work.
  • For any models that have a corresponding database table by using table=True in the class declaration, modify the declaration to ignore class type checking:
class Widget(CruddyModel, table=True):
    pass

becomes

class Widget(CruddyModel, table=True):  # type: ignore
    pass

  1. Schema example attribute has been moved to plural examples. Modify any use of schema_extra which declares example attribute.
  • For any models that leverage schema_extra={"example": "some value}:
class Widget(CruddyModel):
    name: str = Field(schema_extra={"example": "Widget Name"})

becomes

class Widget(CruddyModel):
    name: str = Field(
            schema_extra={
                "json_schema_extra": {
                    "example": "Widget Name"
                }
            }
        )

  1. UTCDateTime has been removed due to pydantic and sqlalchemy incompatibility with this custom type.
  • Modify any model fields depending on cruddy's UTCDateTime to leverage the new field_validator named validate_utc_datetime:
from sqlmodel import Field, Column, DateTime
from fastapi_cruddy_framework import CruddyModel, UTCDateTime


class Widget(CruddyModel):
    event_date: UTCDateTime | None = Field(
        sa_column=Column(DateTime(timezone=True), nullable=True)
    )

becomes

from typing import Any
from datetime import datetime
from pydantic import field_validator
from sqlmodel import Field, Column, DateTime
from fastapi_cruddy_framework import CruddyModel, validate_utc_datetime


class Widget(CruddyModel):
    event_date: datetime | None = Field(
        default=None,
        sa_column=Column(
            DateTime(timezone=True),
            nullable=True,
            index=True,
            default=None,
        ),
    )

    @field_validator("event_date", mode="before")
    @classmethod
    def validate_event_date(cls, v: Any) -> datetime | None:
        return validate_utc_datetime(v, allow_none=True)

  1. pydantic 2+, and therefore sqlmodel, is more strict with Optional fields.
  • Modify any models with Optional fields to conform to pydantic verison 2+'s stricter possible empty value declarations:
from sqlmodel import Field
from fastapi_cruddy_framework import CruddyModel

# Style 1
class Widget(CruddyModel):
    name: str | None = Field()


# Style 2
class Foo(CruddyModel):
    name: str | None

becomes

from sqlmodel import Field
from fastapi_cruddy_framework import CruddyModel


# Style 1
class Widget(CruddyModel):
    name: str | None = Field(default=None)


# Style 2
class Foo(CruddyModel):
    name: str | None = None

  1. (Optional) Take advantage of new checkers and validators that ship with fastapi-cruddy-framework, courtesy of the validator-collection. For additional documentation on what is available, see that project's README. This change also provides an alternative to the pydantic EmailStr type which is no longer available for use in pydantic 2.0 + sqlalchemy 2.0.
  • Modify any models that might now use validator-collection via importing cruddy's exports:
# various imports ...

class Widget(CruddyModel):
    some_field: str | None = Field(default=None)

    @field_validator("some_field", mode="before")
    @classmethod
    def validate_some_field(cls, v: str) -> str | None:
        # ... some custom e-mail address validation logic
        return v

becomes

# various imports ...
from fastapi_cruddy_framework import field_checkers, field_validators, field_errors
# field_validators = validators
# field_checkers = checkers
# field_errors = errors


class Widget(CruddyModel):
    some_field: str | None = Field(default=None)

    @field_validator("some_field", mode="before")
    @classmethod
    def validate_some_field(cls, v: str) -> str | None:
        return field_validators.email(v)

  1. pydantic 2.0+ replaces record.dict() with record.model_dump().
  • Modify all calls to .dict() by replacing with .model_dump():
record_dict = record.dict()

becomes

record_dict = record.model_dump()

  1. pydantic 2.0+ moves the default Undefined field, and all other fields, to pydantic_core.
  • Modify all references to Undefined or other fields:
from pydantic.fields import Undefined

becomes

from pydantic_core import PydanticUndefined as Undefined

This applies to all custom pydantic fields

  1. Model class level fields are no longer accessed via the private attribute __fields__.
  • Modify all references to __fields__:
my_model.__fields__

becomes

my_model.model_fields

  1. Cruddy's BulkDTO type alters the data attribute's primary type, in accordance with the new return value from sqlalchemy 2.0+.
  • Modify any code directly using a BulkDTO object to change type dependence:
class BulkDTO(CruddyGenericModel):
    total_pages: int
    total_records: int
    limit: int
    page: int
    data: list[Row]

becomes

class BulkDTO(CruddyGenericModel):
    total_pages: int
    total_records: int
    limit: int
    page: int
    data: Sequence[Row]

  1. As of December 2023, sqlmodel does not support lambda functions when declaring an sa_column Field argument. As a result, created_at and updated_at fields have been separated from core cruddy models and isolated into a functional class generator mixin. This allows framework users to achieve the same effect without creating sqlalchemy errors because multiple models are trying to share the same Column instance.
  • Modify any code where a table=true model expects the normal cruddy created_at and updated_at timestamping functionality:
from fastapi_cruddy_framework import CruddyIntIDModel, CruddyUUIDModel
from sqlmodel import Field

class Widget(CruddyUUIDModel, table=True):
    name: str = Field(nullable=False, index=True)


class Foo(CruddyIntIDModel, table=True):
    name: str = Field(nullable=False, index=True)

becomes

# The "type: ignore" comments are for migration note 3, ABOVE ^
from fastapi_cruddy_framework import CruddyIntIDModel, CruddyUUIDModel, CruddyCreatedUpdatedMixin
from sqlmodel import Field


class Widget(CruddyCreatedUpdatedMixin(), CruddyUUIDModel, table=True):  # type: ignore
    name: str = Field(nullable=False, index=True)


class Foo(CruddyCreatedUpdatedMixin(), CruddyIntIDModel, table=True):  # type: ignore
    name: str = Field(nullable=False, index=True)

(back to top)