Getting error TypeError: 'SQLModelMetaclass' object is not iterable

Viewed 39

I have a fastapi project using SQLModel that has JWT auth active. I have a registration endpoint and am trying to return a subset of the user record when a post to the registration endpoint is successful. I seem to be able to return the 'User' model which is tied to an actual table but I don't want to return the password hash so I created a new model called 'SensitiveUser' not tied to any table with the password removed. However, when I try to use this model and return the data from the endpoint I get the error "TypeError: 'SQLModelMetaclass' object is not iterable". Any insight on why this is happening is appreciated. I'm struggling to find this exact issue elsewhere online. Thanks.

Repo:

# Return all users.
def select_all_users():
    with Session(rms_engine) as session:
        statement = select(User)
        res = session.exec(statement).all()
        return res

# Returns a specific user record.
def find_user(name):
    with Session(rms_engine) as session:
        statement = select(User).where(User.username == name)
        return session.exec(statement).first()

# Returns a specific user record withou password information.
def find_sensitive_user(name):
    with Session(rms_engine) as session:
        statement = select(SensitiveUser).where(SensitiveUser.username == name)
        return session.exec(statement).first()

Models:

class User(SQLModel, table=True):
    id: Optional[int] = Field(primary_key=True)
    username: str
    password: str = Field(max_length=256, min_length=6)
    email: EmailStr
    created_at: datetime.datetime = datetime.datetime.now()
    is_admin: bool = False

class SensitiveUser(SQLModel):
    id: int
    username: str
    email: str
    created_at: datetime.datetime

The code below does work but returns more info than I want, namely the password:

@user_router.post('/register', status_code=201, tags=['users'], description='Register a new user')
def register(user: UserInput):
    users = select_all_users()
    if any(x.username == user.username for x in users):
        raise HTTPException(status_code=400, detail='Username is taken')
    hashed_pwd = auth_handler.get_password_hash(user.password)
    u = User(username=user.username, password=hashed_pwd, email=user.email, is_admin=user.is_admin)
    rms_session.add(u)
    rms_session.commit()
    stored_user = find_user(user.username) 
    return stored_user

The code below returns the "TypeError: 'SQLModelMetaclass' object is not iterable" error:

@user_router.post('/register', status_code=201, tags=['users'], description='Register a new user')
def register(user: UserInput):
    users = select_all_users()
    if any(x.username == user.username for x in users):
        raise HTTPException(status_code=400, detail='Username is taken')
    hashed_pwd = auth_handler.get_password_hash(user.password)
    u = User(username=user.username, password=hashed_pwd, email=user.email, is_admin=user.is_admin)
    rms_session.add(u)
    rms_session.commit()
    stored_user = find_sensitive_user(user.username) # <--- Error Occurs Here
    return stored_user
1 Answers

The thing is that you cannot select an object that has not been mapped to the database's table, and SensitiveUser's objects are not! You should extend your User class to produce SensitiveUsers, like this:

class User(SQLModel, table=True):
    id: Optional[int] = Field(primary_key=True)
    username: str
    password: str = Field(max_length=256, min_length=6)
    email: str
    created_at: datetime.datetime = datetime.datetime.now()
    is_admin: bool = False

    def to_sensitive_user(self) -> SensitiveUser:
        return SensitiveUser(id=self.id, username=self.username, 
                             email=self.email, created_at=self.created_at)

And then, you can use find_user function extended with the call to to_sensitive_user(), like this:

stored_user = find_user(user.username).to_sensitive_user()

Of course, here needs to be some is-not-None checking and SensitiveUser needs to be defined before User, as well.

Related