awtkns / fastapi-crudrouter

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

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

Open cobycloud opened 11 months ago

cobycloud commented 11 months 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 8 months 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 7 months 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 4 months ago

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

Acutapugione commented 4 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