Search code examples
python-3.xalembic

NameError: name 'User' is not defined (Python @classmethod not recognizing class defintion)


Using Python 3.11 with SQLAlchemy==2.0.18, alembic==1.11.1, fastapi==0.100.0, pydantic==2.0.2

The class is defined above, with a classmethod below it.

User.py

from .base import Base

class User(Base):
__tablename__ = "users"

    id: Mapped[UUID] = mapped_column(
        "id", nullable=False, unique=True, primary_key=True
    )
    first_name: Mapped[str] = mapped_column("first_name", String(length=64), nullable=False)
    last_name: Mapped[str] = mapped_column("last_name", String(length=64), nullable=False)
    # items = relationship("Item", back_populates="owner")
    
    @classmethod
    async def delete(cls, session: AsyncSession, user: User) -> None:
        await session.delete(user)
        await session.flush()

Base.py

from sqlalchemy import MetaData
from sqlalchemy.orm import DeclarativeBase

convention = {
    "ix": "ix_%(column_0_label)s",
    "uq": "uq_%(table_name)s_%(column_0_name)s",
    "ck": "ck_%(table_name)s_%(constraint_name)s",
    "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
    "pk": "pk_%(table_name)s",
}


class Base(DeclarativeBase):
    __abstract__ = True
    metadata = MetaData(naming_convention=convention)

    def __repr__(self) -> str:
        columns = ", ".join(
            [f"{k}={repr(v)}" for k, v in self.__dict__.items() if not k.startswith("_")]
        )
        return f"<{self.__class__.__name__}({columns})>"

However, this yields the following error: NameError: name 'User' is not defined

For reference, I'm working off of the following project, which builds and works correctly: https://github.com/rhoboro/async-fastapi-sqlalchemy/blob/main/app/api/notes/schema.py

Not sure what's happening here.

I'm expecting the class definition to allow for type-hinting for what the delete classmethod returns.


Solution

  • class User:
        @classmethod
        def foo(cls, user: User):
            pass
    

    This code doesn't work, since user: User is referencing the User class while it's still being defined.

    You'll have to add another import:

    from __future__ import annotations
    
    class User:
        @classmethod
        def foo(cls, user: User):
            pass