pythonsqlalchemypython-typing

sql alchemy constructor typesafety


in order to have type safety in SQLAlchemy, do I need to define custom constructors for each model? With the current setup there is no type safety at all when creating an object

from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "user"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    fullname: Mapped[str]


user = User(namew="Alice", fullname="Alice Smith")

enter image description here


Solution

  • The default constructor for ORM models is not strictly typed. However in SQLAlchemy 2.0+ it is possible to map model classes as dataclasses, which will provide a typed constructor.

    from sqlalchemy.orm import MappedAsDataclass
    ...
    
    
    class User(MappedAsDataclass, Base):                                                                                                                                                                           
        __tablename__ = "user"                                                                                                                                                                                         
                                                                                                                                                                                                                       
        id: Mapped[int] = mapped_column(init=False, primary_key=True)                                                                                                                                                  
        name: Mapped[str]                                                                                                                                                                                              
        fullname: Mapped[str]   
    
    user = User(namew="Alice", fullname=b"Alice Smith") 
    

    Running mypy --strict on the above source (with all the imports etc configured) results in this output:

    so79432772.py:18: error: Unexpected keyword argument "namew" for "User"; did you mean "name"?  [call-arg]
    so79432772.py:18: error: Argument "fullname" to "User" has incompatible type "bytes"; expected "SQLCoreOperations[str] | str"  [arg-type]
    Found 2 errors in 1 file (checked 1 source file)