Schema与迁移 #
一、Schema详解 #
1.1 Schema定义 #
elixir
defmodule Hello.Accounts.User do
use Ecto.Schema
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
schema "users" do
field :email, :string
field :name, :string
field :age, :integer
field :active, :boolean, default: true
field :bio, :string
field :settings, :map, default: %{}
field :tags, {:array, :string}, default: []
timestamps(type: :utc_datetime)
end
end
1.2 字段选项 #
elixir
schema "users" do
field :email, :string
field :name, :string, default: "Anonymous"
field :age, :integer, default: 0
field :password, :string, virtual: true
field :metadata, :map, default: %{}
field :scores, {:array, :integer}, default: []
field :inserted_at, :naive_datetime
field :updated_at, :naive_datetime
end
1.3 嵌入Schema #
elixir
defmodule Hello.Accounts.Address do
use Ecto.Schema
embedded_schema do
field :street, :string
field :city, :string
field :zip, :string
field :country, :string
end
end
defmodule Hello.Accounts.User do
use Ecto.Schema
schema "users" do
field :email, :string
embeds_one :address, Hello.Accounts.Address
embeds_many :phone_numbers, Hello.Accounts.PhoneNumber
end
end
二、数据库迁移 #
2.1 创建迁移 #
bash
mix ecto.gen.migration create_users
2.2 迁移文件 #
elixir
defmodule Hello.Repo.Migrations.CreateUsers do
use Ecto.Migration
def change do
create table(:users) do
add :email, :string, null: false
add :name, :string
add :age, :integer
add :active, :boolean, default: true
add :bio, :text
timestamps()
end
create unique_index(:users, [:email])
create index(:users, [:name])
end
end
2.3 执行迁移 #
bash
mix ecto.migrate
mix ecto.rollback
mix ecto.reset
三、表操作 #
3.1 创建表 #
elixir
def change do
create table(:users) do
add :email, :string, null: false
add :name, :string
add :age, :integer
timestamps()
end
end
3.2 删除表 #
elixir
def change do
drop table(:users)
end
3.3 修改表 #
elixir
def change do
alter table(:users) do
add :phone, :string
modify :name, :string, null: false
remove :age
end
end
3.4 重命名 #
elixir
def change do
rename table(:users), :email, to: :email_address
end
四、字段类型 #
4.1 基本类型 #
| 迁移类型 | Elixir类型 | 说明 |
|---|---|---|
| :integer | :integer | 整数 |
| :bigint | :integer | 大整数 |
| :float | :float | 浮点数 |
| :boolean | :boolean | 布尔值 |
| :string | :string | 字符串(255) |
| :text | :string | 长文本 |
| :binary | :binary | 二进制 |
| :date | :date | 日期 |
| :time | :time | 时间 |
| :naive_datetime | :naive_datetime | 日期时间 |
| :utc_datetime | :utc_datetime | UTC日期时间 |
| :map | :map | JSON/Map |
| 数组 |
4.2 字段选项 #
elixir
create table(:users) do
add :email, :string, null: false
add :name, :string, default: "Anonymous"
add :age, :integer, default: 0
add :uuid, :binary_id, primary_key: true
add :price, :decimal, precision: 10, scale: 2
add :tags, {:array, :string}, default: []
end
五、索引 #
5.1 创建索引 #
elixir
def change do
create index(:users, [:name])
create unique_index(:users, [:email])
create index(:users, [:name, :email])
end
5.2 条件索引 #
elixir
def change do
create index(:users, [:email],
where: "active = true"
)
end
5.3 删除索引 #
elixir
def change do
drop index(:users, [:name])
end
六、外键约束 #
6.1 添加外键 #
elixir
create table(:comments) do
add :post_id, references(:posts, on_delete: :delete_all)
add :user_id, references(:users, on_delete: :nilify_all)
add :body, :text
timestamps()
end
6.2 外键选项 #
| 选项 | 说明 |
|---|---|
| :nothing | 无操作(默认) |
| :delete_all | 删除所有关联记录 |
| :nilify_all | 设置外键为NULL |
| :restrict | 阻止删除 |
6.3 添加约束 #
elixir
create constraint(:users, :price_must_be_positive, check: "price >= 0")
七、关联关系Schema #
7.1 belongs_to #
elixir
defmodule Hello.Blog.Comment do
use Ecto.Schema
schema "comments" do
field :body, :string
belongs_to :post, Hello.Blog.Post
belongs_to :user, Hello.Accounts.User
end
end
7.2 has_many #
elixir
defmodule Hello.Blog.Post do
use Ecto.Schema
schema "posts" do
field :title, :string
field :body, :text
has_many :comments, Hello.Blog.Comment
belongs_to :author, Hello.Accounts.User
end
end
7.3 has_one #
elixir
defmodule Hello.Accounts.User do
use Ecto.Schema
schema "users" do
field :email, :string
has_one :profile, Hello.Accounts.Profile
end
end
7.4 many_to_many #
elixir
defmodule Hello.Blog.Post do
use Ecto.Schema
schema "posts" do
field :title, :string
many_to_many :tags, Hello.Blog.Tag,
join_through: "posts_tags",
on_replace: :delete
end
end
八、迁移最佳实践 #
8.1 可逆迁移 #
elixir
def change do
create table(:users) do
add :email, :string
end
end
8.2 不可逆迁移 #
elixir
def up do
execute "CREATE EXTENSION IF NOT EXISTS citext"
alter table(:users) do
modify :email, :citext
end
end
def down do
alter table(:users) do
modify :email, :string
end
end
8.3 数据迁移 #
elixir
def up do
alter table(:users) do
add :full_name, :string
end
flush()
from(u in User, select: u)
|> Repo.all()
|> Enum.each(fn user ->
full_name = "#{user.first_name} #{user.last_name}"
Repo.update_all(from(u in User, where: u.id == ^user.id),
set: [full_name: full_name]
)
end)
alter table(:users) do
remove :first_name
remove :last_name
end
end
九、生成器 #
9.1 生成Schema和迁移 #
bash
mix phx.gen.schema Accounts User users email:string name:string age:integer
9.2 生成HTML资源 #
bash
mix phx.gen.html Accounts User users email:string name:string
9.3 生成JSON资源 #
bash
mix phx.gen.json Accounts User users email:string name:string
9.4 生成LiveView资源 #
bash
mix phx.gen.live Accounts User users email:string name:string
十、Context模式 #
10.1 Context定义 #
elixir
defmodule Hello.Accounts do
alias Hello.Repo
alias Hello.Accounts.User
def list_users, do: Repo.all(User)
def get_user!(id), do: Repo.get!(User, id)
def create_user(attrs) do
%User{}
|> User.changeset(attrs)
|> Repo.insert()
end
def update_user(%User{} = user, attrs) do
user
|> User.changeset(attrs)
|> Repo.update()
end
def delete_user(%User{} = user) do
Repo.delete(user)
end
def change_user(%User{} = user, attrs \\ %{}) do
User.changeset(user, attrs)
end
end
10.2 目录结构 #
text
lib/hello/
├── accounts/
│ ├── accounts.ex # Context模块
│ ├── user.ex # User Schema
│ └── user_token.ex # UserToken Schema
└── blog/
├── blog.ex # Context模块
├── post.ex # Post Schema
└── comment.ex # Comment Schema
十一、总结 #
11.1 核心概念 #
| 概念 | 说明 |
|---|---|
| Schema | 数据模型定义 |
| Migration | 数据库迁移 |
| Field | 字段定义 |
| Association | 关联关系 |
| Index | 索引 |
11.2 常用命令 #
| 命令 | 说明 |
|---|---|
| mix ecto.gen.migration | 生成迁移 |
| mix ecto.migrate | 执行迁移 |
| mix ecto.rollback | 回滚迁移 |
| mix ecto.reset | 重置数据库 |
11.3 下一步 #
现在你已经了解了Schema与迁移,接下来让我们学习 查询操作,深入了解Ecto查询语言!
最后更新:2026-03-28