LoginSignup
1
0

More than 1 year has passed since last update.

【SQLAlchemy】SQLAlchemy のモデルの型ヒント ( Type Annotation ) を理解する

Posted at

TL;DR

from sqlalchemy import DateTime, Column, func
from sqlalchemy.orm import Mapped, declarative_base

Base = declarative_base()


class ModelName1(Base):
    created_at: Mapped[datetime.datetime] = Column(
        DateTime,
        default=func.now(),
        nullable=False
    )

のように、Mapped[<マップされる型>] とすればよい。

きっかけ

本記事では、SQLAlchemy を使用したモデルに与える、正しい型ヒントを紹介する。

SQLAlchemy を利用しての開発中に、SQLAlchemy の型ヒントがうまく与えられていないことがわかった。

試しに、column_name: Column = Column() とするとマイグレーション時に以下のような例外が送出される。

sqlalchemy.exc.ArgumentError: Type annotation for "ModelName.uid" can't be
correctly interpreted for Annotated Declarative Table form.
ORM annotations should normally make use of the ``Mapped[]`` generic type,
or other ORM-compatible generic type, as a container for the actual type,
which indicates the intent that the attribute is mapped.
Class variables that are not intended to be mapped by the ORM should use ClassVar[].
To allow Annotated Declarative to disregard legacy annotations which don't
use Mapped[] to pass, set "__allow_unmapped__ = True" on the class or a
superclass this class. (Background on this error at: https://sqlalche.me/e/20/zlpr)

これを翻訳すると、以下のようになる。

sqlalchemy.exc.ArgumentError: "ModelName.uid"の型ヒントは、 Annotated Declarative Table 形式に対して正しく解釈できません。
通常、ORMアノテーションは、実際の型のコンテナとしてMapped[]ジェネリック型、
または他のORM互換のジェネリック型を使用する必要があり、属性がマッピングされることを示します。
ORMによってマッピングされることを意図していないクラス変数は、ClassVar[]を使用してください。
Annotated DeclarativeがMapped[]を使用しないレガシーアノテーションを無視するようにするには、
クラスまたはそのクラスのスーパークラスに"allow_unmapped = True"を設定してください。 (このエラーの背景: https://sqlalche.me/e/20/zlpr)

要するに、ORM にマッピングされる Column は、Mapped[str] のような形で Mapped[<実際にマップされる型>] のようにすればよい。

Solution

Column の場合

from datetime import datetime

from sqlalchemy import String, Column, func
from sqlalchemy.orm import Mapped, declarative_base

Base = declarative_base()


class ModelName1(Base):
    __tablename__: str = "model_name"

    created_at: Mapped[datetime.datetime] = Column(
        DateTime,
        default=func.now(),
        nullable=False
    )

    updated_at: Mapped[datetime.datetime] = Column(
        DateTime,
        default=func.now(),
        nullable=False
    )

    data: Mapped[str] = Column(
        String,
        nullable=False
    )

relationship の場合

from sqlalchemy.orm import Mapped, relationship, declarative_base

Base = declarative_base()


class ModelName1(Base):
    model_name_2: Mapped["ModelName2"] = relationship(
        "ModelName2",
        back_populates="reply"
    )


class ModelName2(Base):
    model_name_1: Mapped["ModelName1"] = relationship(
        "ModelName1",
        back_populates="model_name_2",
        uselist=False
    )

Mapped[] に与える引数は、str 型の値でも、実際の Model でも構わない。
しかしながら、大抵のモデルは相互依存になるだろううから、str 型が無難だろう。

References

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0