21

I'm getting this error, the models migrate successfully when i run uvicorn main:app --reload but when i tried to go to 127.0.0.1:8000/docs i got this error. I'm using Postgresql for the database

TypeError: Object of type 'ModelMetaclass' is not JSON serializable

This is my file structure

backend/main.py
backend/pydantic_models.py
backend/requirements.txt
backend/sql
backend/sql/crud.py
backend/sql/database.py
backend/sql/sql_models.py
backend/sql/__init__.py

I followed the tutorial on https://fastapi.tiangolo.com/tutorial/sql-databases/ Here is the code. main.py

from typing import List

from fastapi import FastAPI, Depends, HTTPException, status
from sqlalchemy.orm import Session

from sql import crud, database, sql_models
from pydantic_models import User, Todo, UserCreation, TodoCreation


sql_models.Base.metadata.create_all(bind=database.engine)

app = FastAPI()

def get_db():
    db = database.SessionLocal()
    try:
        yield db
    finally:
        db.close()


@app.post("/users/", response_model=User)
def create_user(user=User, db: Session = Depends(get_db)):
    db_user_email = crud.get_user_by_email(db, email=user.email)
    db_user_name = crud.get_user
    if db_user:
        raise HTTPException(status_code=status.HTTP_400_BAD,
                            detail="Email already taken")
    return crud.create_user(db=db, user=user)


@app.get("/users/", response_model=List[User])
def read_all_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    users = crud.get_all_users(db, skip=skip, limit=limit)
    return users


@app.get("/users/{user_id}", response_model=User)
def read_user(user_id: int, db: Session = Depends(get_db)):
    db_user = crud.get_user(db, user_id=user_id)
    if db_user is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
    return db_user


@app.post("/users/{user_id}/todo/", response_model=Todo)
def create_todo_list(user_id: int, todo: TodoCreation, db: Session = Depends(get_db)):
    return crud.create_todo(db=db, todo=todo, user_id=user_id)


@app.get("/items/", response_model=List[Todo])
def read_todo(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    todos = crud.get_todo(db, skip=skip, limit=limit)
    return todos

pydantic_models.py

from typing import List, Optional
from pydantic import BaseModel


# Todo model
class TodoBase(BaseModel):
    title: str
    description: Optional[str] = None

class TodoCreation(TodoBase):
    pass


class Todo(TodoBase):
    id: int
    owner_id: int

    class Config:
        orm_mode = True
        
# User model
class UserBase(BaseModel):
    username: str
    email: str

class UserCreation(UserBase):
    password: str

class User(UserBase):
    id: int
    is_active: bool
    todo: List[Todo] = []

    class Config:
        orm_mode = True

sql.crud.py

from sqlalchemy.orm import Session

from . import sql_models
import pydantic_models

# Get single user
def get_user(db: Session, user_id: int):
    return db.query(sql_models.User).filter(models.User.id == user_id).first()

def get_user_by_name(db: Session, username: str):
    return db.query(sql_models.User).filter(models.User.username == username).first()

# Get user by email
def get_user_by_email(db: Session, email: str):
    return db.query(sql_models.User).filter(models.User.email == email).first()

# Get all users
def get_all_users(db: Session, skip: int = 0, limit: int = 100):
    return db.query(sql_models.User).offset(skip).limit(limit).all()

# Create a user
def create_user(db: Session, user: pydantic_models.UserCreation):
    fake_hashed_password = user.password + "fakehash" # Hashed user's password
    db_user = sql_models.User(username=user.username, email=user.email, password=fake_hashed_password)
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user


def get_todo(db: Session, skip: int = 0, limit: int = 100):
    return db.query(sql_models.Todo).offset(skip).limit(limit).all()


def create_todo(db: Session, todo: pydantic_models.TodoCreation, user_id: int):
    db_item = models.Item(**item.dict(), owner_id=user_id)
    db.add(db_item)
    db.commit()
    db.refresh(db_item)
    return db_item

sql.database.py

import os

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = os.environ["POSTGRES_LINK"]
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

sql.sql_models.py

from sql.database import Base

from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship

class User(Base):
    __tablename__  = "users"

    id = Column(Integer, primary_key=True, index=True)
    username = Column(String, unique=True, index=True)
    email = Column(String, unique=True, index=True)
    password = Column(String)
    is_active = Column(Boolean, default=True)

    todo = relationship("Todo", back_populates="owner")

class Todo(Base):
    __tablename__ = "todo"

    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True)
    description = Column(String, index=True)
    owner_id = Column(Integer, ForeignKey("users.id"))

    owner = relationship("User", back_populates="todo")

here is the full error

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [6844] using statreload
INFO:     Started server process [2484]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     127.0.0.1:51303 - "GET /docs HTTP/1.1" 200 OK
INFO:     127.0.0.1:51303 - "GET /openapi.json HTTP/1.1" 500 Internal Server ErrorERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 388, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\fastapi\applications.py", line 179, in __call__
    await super().__call__(scope, receive, send)
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\starlette\applications.py", line 111, in __call__
    await self.middleware_stack(scope, receive, send)
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\starlette\middleware\errors.py", line 181, in __call__
    raise exc from None
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\starlette\middleware\errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\starlette\exceptions.py", line 82, in __call__
    raise exc from None
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\starlette\exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\starlette\routing.py", line 566, in __call__
    await route.handle(scope, receive, send)
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\starlette\routing.py", line 227, in handle
    await self.app(scope, receive, send)
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\starlette\routing.py", line 41, in app
    response = await func(request)
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\fastapi\applications.py", line 128, in openapi
    return JSONResponse(self.openapi())
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\fastapi\applications.py", line 106, in openapi
    self.openapi_schema = get_openapi(
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\fastapi\openapi\utils.py", line 348, in get_openapi
    result = get_openapi_path(route=route, model_name_map=model_name_map)
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\fastapi\openapi\utils.py", line 177, in get_openapi_path
    operation_parameters = get_openapi_operation_parameters(
  File "c:\users\bryan\source\repos\todoapp\backend\env\lib\site-packages\fastapi\openapi\utils.py", line 95, in get_openapi_operation_parameters
    "schema": field_schema(
  File "pydantic\schema.py", line 184, in pydantic.schema.field_schema
  File "pydantic\schema.py", line 767, in pydantic.schema.encode_default
  File "pydantic\json.py", line 62, in pydantic.json.pydantic_encoder
TypeError: Object of type 'ModelMetaclass' is not JSON serializable
1
  • 1
    This answer might prove helpful to future readers Commented Jun 12, 2023 at 14:49

7 Answers 7

41

I triggered the same error message by effectively misplacing the response_model=User in the function def instead of the decorator.

Wrong:

@app.post("/users/")
def create_user(user: User, db: Session = Depends(get_db), response_model=User):
    db_user_email = crud.get_user_by_email(db, email=user.email)

Correct:

@app.post("/users/", response_model=User)
def create_user(user: User, db: Session = Depends(get_db)):
    db_user_email = crud.get_user_by_email(db, email=user.email)
Sign up to request clarification or add additional context in comments.

1 Comment

though this isn't the answer to the OP's question, this is the first result on google for this error + fastapi, and it happened to be the solution for me!
23

I think the problem occurred at

@app.post("/users/", response_model=User)
def create_user(user=User, db: Session = Depends(get_db)):
    db_user_email = crud.get_user_by_email(db, email=user.email)
    db_user_name = crud.get_user
    if db_user:
        raise HTTPException(status_code=status.HTTP_400_BAD,
                            detail="Email already taken")
    return crud.create_user(db=db, user=user)

in the main.py file.

Changing user=User to user: User worked fine on mine!

1 Comment

Nice catch @Brothersoo. I was not able to identify this mistake in my code. Minor but important. Thanks for your help.
1

I had a similar error but it was Object of type 'type' is not JSON serializable. At one place, I had a function def of the following kind:

@app.post("/read")
async def read_items(item_name: str = Optional[None]):
    # code omitted
    return 

As you can guess, I had the typing in the function defined wrongly. It should have been:

@app.post("/read")
async def read_items(item_name: Optional[str] = None):
    # code omitted
    return 

Comments

1

You need to different between pydantic and sqlachemy way of representing models. For Pydantic we make use of : while for SqlAlchemy we use =. For your case you need to change user=User to user:User because you are representing Pydantic models

Comments

0

I had the same issue because of not returning the appropriate content in the response.

Fixed by replacing:

return JSONResponse(status_code=status.HTTP_200_OK,
                                content=MyModel(status="something"))

With (notice the .dict()):

return JSONResponse(status_code=status.HTTP_200_OK,
                                content=MyModel(status="something").dict())

1 Comment

Please note that if MyModel contains objects that are not JSON serializable, such as datetime objects, you would either need to convert them to a JSON serializable object (e.g., str) on your own before returning the JSONResponse, or use FastAPI's jsonable_encoder. Please have a look at this answer for more details and examples.
0

What helped me was removing all of the response_models and the returnable response types (aka changing: def smthng(user: User) -> UserResponse: to def smthng(user: User):) from the end of the functions. Of course this way you can't really tell the response type, but if you don't have to it's a good workaround.

Comments

0

Another way to run into this error is misspelling a key in the responses keyword argument of the path:

from fastapi import FastAPI
from pydantic import BaseModel


class Foo(BaseModel):
    bar: str


app = FastAPI()


@app.get("/", responses={200: {"models": Foo}})
def root():
    return Foo(bar="baz")

Where it should be "model" instead of "models". But with the typo, this exact error is thrown when trying to access the openapi.json file:

Traceback (most recent call last):
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py", line 411, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 69, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/fastapi/applications.py", line 1054, in __call__
    await super().__call__(scope, receive, send)
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/starlette/applications.py", line 123, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 65, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/starlette/routing.py", line 756, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/starlette/routing.py", line 776, in app
    await route.handle(scope, receive, send)
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/starlette/routing.py", line 297, in handle
    await self.app(scope, receive, send)
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/starlette/routing.py", line 77, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/starlette/routing.py", line 72, in app
    response = await func(request)
               ^^^^^^^^^^^^^^^^^^^
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/fastapi/applications.py", line 1009, in openapi
    return JSONResponse(self.openapi())
                        ^^^^^^^^^^^^^^
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/fastapi/applications.py", line 981, in openapi
    self.openapi_schema = get_openapi(
                          ^^^^^^^^^^^^
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/fastapi/openapi/utils.py", line 530, in get_openapi
    return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True)  # type: ignore
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/fastapi/encoders.py", line 223, in jsonable_encoder
    obj_dict = _model_dump(
               ^^^^^^^^^^^^
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/fastapi/_compat.py", line 179, in _model_dump
    return model.model_dump(mode=mode, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/$USER/code/models/.venv/lib/python3.11/site-packages/pydantic/main.py", line 347, in model_dump
    return self.__pydantic_serializer__.to_python(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pydantic_core._pydantic_core.PydanticSerializationError: Unable to serialize unknown type: <class 'pydantic._internal._model_construction.ModelMetaclass'>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.