ecto-patterns について
Ectoのスキーマ、クエリ、マイグレーションを扱う開発者向けのリファレンススキルです。安全なデータ処理やクエリ、プリロード、制約の原則と、よくあるアンチパターンをまとめ、詳細資料も案内します。
主な機能
- 01スキーマとチェンジセットの指針
- 02安全なクエリとプリロードのパターン
- 03マイグレーションと制約の原則
- 04よくあるアンチパターンの例
活用シーン
- 01castと内部変更関数の選択
- 02クエリでのSQLインジェクション回避
- 03コレクションのプリロード計画
- 04金額フィールドと関連付けの確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ oliver-kriska/ claude-elixir-phoenix --skill ecto-patternsインストール後、Claude Code で/ecto-patternsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/changesets.mdfulltext-search.mdmigrations.mdqueries.mdtransactions.md詳細
Ecto パターンリファレンス
Ecto のスキーマ、クエリ、マイグレーションを扱うためのリファレンスです。
絶対に守るべき原則 — 違反してはいけません
- 変更セットは外部データ用です — ユーザーや API からの入力には
cast/4、内部の信頼できるデータにはchange/2またはput_change/3を使用します - 金額に
:floatを使ってはいけません — 常に:decimalまたは:integer(セント単位)を使用します - Rails 形式のポリモーフィック関連付けは禁止です — 外部キー制約が機能しなくなるため、複数の NULL 許容外部キーまたは個別の結合テーブルを使用します
- クエリでは常に値をピン留めします —
u.name == ^user_inputは安全ですが、文字列補間は SQL インジェクションの原因になります - 個別のレコードではなく、コレクションをプリロードします — ループ内でのプリロードは N+1 クエリを引き起こします
- 競合状態にはバリデーションより制約を使います — バリデーションはすばやいフィードバックを提供し、制約はデータベースレベルの安全性を提供します
has_manyには個別のクエリ、belongs_toには JOIN を使います — 行の重複を防げます- 暗黙のクロス結合は禁止です —
from(a in A, b in B)はon:がない場合、直積を生成します - 共有データで
cast_assocを使う前に重複を除去します — 複数の親が子データを共有する場合、変更セットを作成する前に子レコードの重複を除去します。重複除去が機能するのは、単一の変更セット内だけです
スキーマのクイックテンプレート
defmodule MyApp.Context.Entity do
use Ecto.Schema
import Ecto.Changeset
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
schema "entities" do
field :name, :string
field :status, Ecto.Enum, values: [:draft, :active, :archived]
field :amount_cents, :integer # Never :float for money!
belongs_to :user, MyApp.Accounts.User
timestamps(type: :utc_datetime_usec)
end
def changeset(entity, attrs) do
entity
|> cast(attrs, [:name, :status, :amount_cents])
|> validate_required([:name])
|> foreign_key_constraint(:user_id)
end
end
クイック判断ガイド
cast と put_change と change
| 関数 | 使用する場面 |
|---|---|
cast/4 | 外部データ(ユーザー入力、API) |
put_change/3 | 内部の信頼できるデータ(タイムスタンプ、計算値) |
change/2 | 既存の構造体からの内部データ |
プリロード戦略
| 関連付け | 戦略 |
|---|---|
belongs_to | JOIN(単一クエリ) |
has_many | 個別のクエリ(行の重複を回避) |
よくあるアンチパターン
| 誤り | 正しい方法 |
|---|---|
field :amount, :float | field :amount_cents, :integer |
"SELECT * WHERE name = '#{name}'" | from(u in User, where: u.name == ^name) |
Repo.all(User) |> Enum.filter(& &1.active) | from(u in User, where: u.active) |
| ループ内でプリロードする | Repo.preload(posts, :comments) |
ユーザー入力を使って Repo.get!(User, user_id) を呼び出す | Repo.get(User, id) + nil を処理する |
Repo.transaction 内で {:ok, _} = Repo.update(cs) を実行する | case/with + Repo.rollback(cs)(transactions.md を参照) |
リファレンス
詳しいパターンについては、以下を参照してください。
${CLAUDE_SKILL_DIR}/references/changesets.md- cast と put_change、カスタムバリデーション、prepare_changes${CLAUDE_SKILL_DIR}/references/queries.md- 合成可能なクエリ、dynamic、サブクエリ、プリロード${CLAUDE_SKILL_DIR}/references/migrations.md- 安全なマイグレーション、同時インデックス作成、NOT NULL${CLAUDE_SKILL_DIR}/references/transactions.md- Repo.transact、Ecto.Multi、アップサート
コメント
Markdown 対応