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