fastapi-crudrouter icon indicating copy to clipboard operation
fastapi-crudrouter copied to clipboard

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

Open cobycloud opened this issue 1 year ago • 4 comments

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_'

cobycloud avatar Aug 06 '23 06:08 cobycloud

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

miko1ann avatar Oct 25 '23 15:10 miko1ann

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

Laurel-rao avatar Nov 27 '23 03:11 Laurel-rao

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

Acutapugione avatar Feb 18 '24 18:02 Acutapugione

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

Acutapugione avatar Feb 18 '24 19:02 Acutapugione