Skip to content

mapped_column と Column の違い

SQLAlchemyのORMを使う際、テーブルカラムを定義する方法としてmapped_column()Column()の2つの方法が存在します。この違いを理解することは、効果的なORMモデル設計に不可欠です。

問題の本質

  • 従来のColumn()はSQLAlchemyのコアレイヤー(SQL式言語)ORMレイヤーの両方で使用されていた
  • この設計により責務が分散し、ORM特有の機能拡張が困難になっていた
  • 混在が原因で型ヒントとの統合や静的解析が制限される問題があった

推奨ソリューション:mapped_columnの採用

SQLAlchemy 2.0以降のベストプラクティス

ORMモデルを定義する場合はmapped_column()の使用が公式推奨されています

推奨: mapped_columnを使用

class User(DeclarativeBase): tablename = "users"

id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column()
age: Mapped[int | None]  # mapped_column省略形
# 非推奨: 従来のColumn使用
class LegacyUser(Base):
    __tablename__ = "legacy_users"
    
    id = Column(Integer, primary_key=True)
    name = Column(String)
    age = Column(Integer, nullable=True)

主要な機能比較

特徴Columnmapped_column
レイヤーの対応コア & ORMORM専用
型ヒント連携不可可能
nullableの自動設定手動Optionalで自動
カラムタイプ推論不可Python型から推論
mypy連携一部完全対応

実用的メリット解説

1. 型ヒントによる自動推論

python
class Product(DeclarativeBase):
    # VARCHARとnullable=Falseを自動設定
    name: Mapped[str]
    
    # BIGINTを推論
    stock: Mapped[int]
    
    # BOOLEAN & nullable=True を自動設定
    is_active: Mapped[bool | None]

2. ボイラープレート削減

python
# 従来 (18文字)
active = Column(Boolean, nullable=False)

# 新しい方法 (9文字)
active: Mapped[bool]

3. 型安全性の向上

python
product = session.get(Product, 1)

# Mappedで定義した場合
print(product.stock + 10)  # 正しい操作

# 定義していない属性は検出可能
print(product.undefined_attr)  # mypyエラー発生

利用上の注意点

混在使用パターン

WARNING

Columnmapped_columnは同じモデル内で併用可能ですが非推奨です。移行中の一時的措置として扱ってください

py
class TransitionModel(DeclarativeBase):
    id: Mapped[int] = mapped_column(primary_key=True)
    
    # 移行中のレガシー定義
    created_at = Column(DateTime)

Columnを今でも使うケース

  1. SQL式言語(コア機能)のみを使う場合
  2. レガシーコードをメンテせずに置換対象外とする場合
  3. 動的カラム生成など高度なメタプログラミングが必要な場合

生成SQLの比較

双方の定義から生成されるSQLテーブルに実質的な違いはありません。機能差はORM層限定です。

/* mapped_columnで生成 */ CREATE TABLE users ( id SERIAL NOT NULL, name VARCHAR NOT NULL, age INTEGER, PRIMARY KEY (id) );

/* Columnで生成 ※同じ出力 */
CREATE TABLE legacy_users (
    id SERIAL NOT NULL,
    name VARCHAR NOT NULL,
    age INTEGER,
    PRIMARY KEY (id)
);

移行ガイド

  1. DeclarativeBaseの新規モデルではmapped_columnを一貫使用
  2. 既存プロジェクト:
    diff
    - from sqlalchemy import Column
    + from sqlalchemy.orm import Mapped, mapped_column
  3. 型ヒント対応:
    diff
    - name = Column(String)
    + name: Mapped[str] = mapped_column()
  4. nullable設定の簡略化:
    diff
    - active = Column(Boolean, nullable=True)
    + active: Mapped[bool | None]

::: success ベネフィットまとめ

  • 開発効率:型ヒント自動設定でコード量50%削減
  • 保守性:静的型チェックが可能に
  • 未来安全性:新機能はmapped_columnに優先実装
  • 学習コスト:Pythonネイティブ型で統一可能 :::