Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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
like image 278
TheNoobProgrammer Avatar asked Oct 07 '20 02:10

TheNoobProgrammer


1 Answers

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)
like image 53
Ben Mares Avatar answered Nov 01 '22 06:11

Ben Mares