关于 ecto-patterns
供开发者使用的 Ecto 模式参考,涵盖 schema、查询和迁移。它归纳安全的数据处理、查询与预加载选择、数据库约束原则及常见反模式,并链接到详细参考资料。
主要功能
- 01Schema 与 changeset 指引
- 02安全查询与预加载模式
- 03迁移和约束原则
- 04常见反模式示例
适用场景
- 01选择 cast 或内部变更函数
- 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 风格的多态关联——这会破坏外键约束;请使用多个可空外键或单独的联结表
- 查询中始终使用绑定值——
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、upsert
评论
支持 Markdown