見出し画像

【FastAPI】Pydanticのorm_modeって何ぞ?って時のメモ

'orm_mode'?あーはいはい、DBと紐付けてくれる便利なやつでしょ、知ってます知ってます、くらいののりで全然公式見てませんでした。すみません。

ORM(オブジェクトリレーションマッピング)のデータ読取に便利

ちょっと面倒だったので、正確さのためにFastAPI公式チュートから抜粋しながらメモってきます。

from typing import List, Optional

from pydantic import BaseModel

# 省略

class User(UserBase):
    id: int
    is_active: bool
    items: List[Item] = []

    class Config:
        orm_mode = True  # ここで値Trueを入れておく、ことでDBからの読取に役立つ! 

It doesn't use : as for the type declarations before.
This is setting a config value, not declaring a type.
Pydantic's orm_mode will tell the Pydantic model to read the data even if it is not a dict, but an ORM model (or any other arbitrary object with attributes).
This way, instead of only trying to get the id value from a dict, as in:

id = data["id"]

it will also try to get it from an attribute, as in:

id = data.id

And with this, the Pydantic model is compatible with ORMs, and you can just declare it in the response_model argument in your path operations.
You will be able to return a database model and it will read the data from it.

Technical Details about ORM mode
SQLAlchemy and many others are by default "lazy loading".
That means, for example, that they don't fetch the data for relationships from the database unless you try to access the attribute that would contain that data.
For example, accessing the attribute items:

current_user.items

would make SQLAlchemy go to the items table and get the items for this user, but not before.
Without orm_mode, if you returned a SQLAlchemy model from your path operation, it wouldn't include the relationship data.
Even if you declared those relationships in your Pydantic models.
But with ORM mode, as Pydantic itself will try to access the data it needs from attributes (instead of assuming a dict), you can declare the specific data you want to return and it will be able to go and get it, even from ORMs.

編集サボった私のせいですが、読みづらくてしょうがないですね。

要は、DBから取得したデータなどに対して、dictとかじゃなくてもそのままAPIに渡せるよ、ということらしい。
例えば↓みたいな感じで、CRUD用の(get_item(db_session, hogehoge)みたいな)utility関数で、DBからデータを(SQLAlchemy経由のORMオブジェクトとして)取得した際に、パスオペレータのresponse_modelに取得したデータをORMオブジェクト(属性を保持するSQLAlchemyのモデル)として、そのままAPIに渡せる(あるいはそこからアプリケーションへとreturnできる)、というのが嬉しいっぽい。

@app.get("/items/", response_model=List[schemas.Item]) 
    def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
        # 別モジュールに定義したCRUD用のutility関数(SQLAlchemyのSessionオブジェクトによってDBへアクセスしてデータを取得)
    items = crud.get_items(db, skip=skip, limit=limit)
        return items

相変わらず、語学力、文章力、論理数理的素養にかけるのでうまくまとめられていないし、多分変なとこあるけど、随時修正していきます。


この記事が気に入ったらサポートをしてみませんか?