awtkns / fastapi-crudrouter

A dynamic FastAPI router that automatically creates CRUD routes for your models
https://fastapi-crudrouter.awtkns.com
MIT License
1.42k stars 157 forks source link

Pydantic V2 incompatibility: FieldInfo object does not have the attribute 'type_' #189

Open cobycloud opened 1 year ago

cobycloud commented 1 year ago

Issue Statement

Incompatible with pydantic v2. Unable to start containerized uvicorn due to AttributeError. This occurs because pydantic's FieldInfo object does not have the attribute 'type_' Please see: pydantic.fields.FieldInfo

This can be observed by adding the following line on startup: print(Potato.__fields__)

The output follows:

{'thickness': FieldInfo(annotation=float, required=True), 'mass': FieldInfo(annotation=float, required=True), 'color': FieldInfo(annotation=str, required=True), 'type': FieldInfo(annotation=str, required=True), 'id': FieldInfo(annotation=int, required=True)}

Code

from sqlalchemy import Column, String, Float, Integer
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

from pydantic import BaseModel
from fastapi import FastAPI
from fastapi_crudrouter import SQLAlchemyCRUDRouter

app = FastAPI()
engine = create_engine(
    "sqlite:///./app.db",
    connect_args={"check_same_thread": False}
)

SessionLocal = sessionmaker(
    autocommit=False,
    autoflush=False,
    bind=engine
)

Base = declarative_base()

def get_db():
    session = SessionLocal()
    try:
        yield session
        session.commit()
    finally:
        session.close()

class PotatoCreate(BaseModel):
    thickness: float
    mass: float
    color: str
    type: str

class Potato(PotatoCreate):
    id: int

    class Config:
        orm_mode = True

class PotatoModel(Base):
    __tablename__ = 'potatoes'

    id = Column(Integer, primary_key=True, index=True)
    thickness = Column(Float)
    mass = Column(Float)
    color = Column(String)
    type = Column(String)

Base.metadata.create_all(bind=engine)

print(Potato.__fields__)

router = SQLAlchemyCRUDRouter(
    schema=Potato,
    create_schema=PotatoCreate,
    db_model=PotatoModel,
    db=get_db,
    prefix='potato'
)

app.include_router(router)

Error


>|  File "/app/src/main.py", line 63, in <module>
>|    router = SQLAlchemyCRUDRouter(
>| File "/usr/local/lib/python3.8/site-packages/fastapi_crudrouter/core/sqlalchemy.py", line 51, in __init__
>|     self._pk_type: type = _utils.get_pk_type(schema, self._pk)
>|   File "/usr/local/lib/python3.8/site-packages/fastapi_crudrouter/core/_utils.py", line 17, in get_pk_type
>|     return schema.__fields__[pk_field].type_
>| AttributeError: 'FieldInfo' object has no attribute 'type_'
miko1ann commented 1 year ago

I got this issue. And my workaround is patch two methods:

patch_crudrouter

def get_pk_type_patch(schema: Type[PYDANTIC_SCHEMA], pk_field: str) -> Any:
    try:
        return schema.__fields__[pk_field].annotation
    except KeyError:
        return int

from pydantic import create_model
def schema_factory_patch(
    schema_cls: Type[T], pk_field_name: str = "id", name: str = "Create"
) -> Type[T]:
    """
    Is used to create a CreateSchema which does not contain pk
    """

    fields = {
        name: (f.annotation, ...)
        for name, f in schema_cls.__fields__.items()
        if name != pk_field_name
    }

    name = schema_cls.__name__ + name
    schema: Type[T] = create_model(__model_name=name, **fields)  # type: ignore
    return schema

Injection

import fastapi_crudrouter
fastapi_crudrouter.core._utils.get_pk_type = get_pk_type_patch
fastapi_crudrouter.core._utils.schema_factory = schema_factory_patch
fastapi_crudrouter.core._base.schema_factory = schema_factory_patch
Laurel-rao commented 1 year ago

it works

import fastapi_crudrouter
fastapi_crudrouter.core._utils.get_pk_type = get_pk_type_patch
fastapi_crudrouter.core._utils.schema_factory = schema_factory_patch
fastapi_crudrouter.core._base.schema_factory = schema_factory_patch
Acutapugione commented 9 months ago

It helps: https://github.com/awtkns/fastapi-crudrouter/issues/189#issuecomment-1779496250 Thanks a lot

Acutapugione commented 9 months ago

I took this part for my diploma) from pydantic import create_model def schema_factory_patch( schema_cls: Type[T], pk_field_name: str = "id", name: str = "Create" ) -> Type[T]: """ Is used to create a CreateSchema which does not contain pk """

fields = {
    name: (f.annotation, ...)
    for name, f in schema_cls.__fields__.items()
    if name != pk_field_name
}

name = schema_cls.__name__ + name
schema: Type[T] = create_model(__model_name=name, **fields)  # type: ignore
return schema