elixir-phoenix-ecto-patterns
Apply Ecto patterns for schemas, changesets, queries, migrations, and preloads.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Apply Ecto patterns for schemas, changesets, queries, migrations, and preloads.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Recommend the right `$elixir-phoenix-*` skill for the current task.
Elixir/Phoenix: Review lifecycle, state-machine, Oban, persistence, pause/resume, retry, and restart-sensitive changes before commit, push, or PR. Use for concurrency-sensitive runtime work to produce explicit blocking vs optional findings, require durability checks, and verify smoke plus restart resilience when applicable.
Capture a solved Phoenix problem as a reusable solution doc.
Audit LiveView assigns for memory bloat, dead assigns, and stream candidates.
Audit project health across architecture, security, performance, tests, and deps.
Analyze Phoenix context boundaries and coupling with `mix xref`.
| name | elixir-phoenix-ecto-patterns |
| description | Apply Ecto patterns for schemas, changesets, queries, migrations, and preloads. |
| metadata | {"short-description":"Apply Ecto schema and query patterns"} |
Reference for working with Ecto schemas, queries, and migrations.
cast/4 for user/API input, change/2 or put_change/3 for internal trusted data:float FOR MONEY — Always use :decimal or :integer (cents)u.name == ^user_input is safe, string interpolation causes SQL injectionhas_many, JOIN FOR belongs_to — Avoids row multiplicationfrom(a in A, b in B) without on: creates Cartesian productcast_assoc WITH SHARED DATA — When multiple parents share child data, deduplicate child records BEFORE building changesets. Dedup only works within a single changesetdefmodule 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
| Function | Use When |
|---|---|
cast/4 | External data (user input, API) |
put_change/3 | Internal trusted data (timestamps, computed) |
change/2 | Internal data from existing struct |
| Relationship | Strategy |
|---|---|
belongs_to | JOIN (single query) |
has_many | Separate queries (avoid row multiplication) |
| Wrong | Right |
|---|---|
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) |
| Preloading in loops | Repo.preload(posts, :comments) |
Repo.get!(User, user_id) with user input | Repo.get(User, id) + handle nil |
For detailed patterns, see:
references/changesets.md - cast vs put_change, custom validations, prepare_changesreferences/queries.md - Composable queries, dynamic, subqueries, preloadingreferences/migrations.md - Safe migrations, concurrent indexes, NOT NULLreferences/transactions.md - Repo.transact, Ecto.Multi, upserts