Ecto Schemas
Master Ecto schema definitions to model your database entities effectively in Elixir applications.
Basic Schema Definition
defmodule MyApp.Accounts.User do use Ecto.Schema import Ecto.Changeset
schema "users" do field :email, :string field :name, :string field :age, :integer field :is_active, :boolean, default: true field :role, Ecto.Enum, values: [:user, :admin, :moderator]
timestamps()
end
def changeset(user, attrs) do user |> cast(attrs, [:email, :name, :age, :is_active, :role]) |> validate_required([:email, :name]) |> validate_format(:email, ~r/@/) |> validate_number(:age, greater_than: 0) |> unique_constraint(:email) end end
Field Types
schema "products" do
Basic types
field :name, :string field :price, :decimal field :quantity, :integer field :is_available, :boolean field :rating, :float
Date and time
field :released_on, :date field :sale_starts_at, :naive_datetime field :sale_ends_at, :utc_datetime
Complex types
field :tags, {:array, :string} field :metadata, :map field :settings, {:map, :string} # map with string values
Binary data
field :image_data, :binary field :uuid, :binary_id
timestamps() end
Associations
defmodule MyApp.Blog.Post do use Ecto.Schema
schema "posts" do field :title, :string field :body, :string
# Belongs to
belongs_to :author, MyApp.Accounts.User
# Has many
has_many :comments, MyApp.Blog.Comment
# Has one
has_one :featured_image, MyApp.Blog.Image
# Many to many
many_to_many :tags, MyApp.Blog.Tag, join_through: "posts_tags"
timestamps()
end end
defmodule MyApp.Blog.Comment do use Ecto.Schema
schema "comments" do field :content, :string
belongs_to :post, MyApp.Blog.Post
belongs_to :user, MyApp.Accounts.User
timestamps()
end end
Embedded Schemas
defmodule MyApp.Accounts.Address do use Ecto.Schema
Embedded schema - stored as JSON in parent
embedded_schema do field :street, :string field :city, :string field :state, :string field :zip, :string field :country, :string, default: "US" end
def changeset(address, attrs) do address |> cast(attrs, [:street, :city, :state, :zip, :country]) |> validate_required([:street, :city, :state, :zip]) end end
defmodule MyApp.Accounts.User do use Ecto.Schema
schema "users" do field :name, :string
# Single embedded
embeds_one :billing_address, MyApp.Accounts.Address, on_replace: :update
# Multiple embedded
embeds_many :shipping_addresses, MyApp.Accounts.Address, on_replace: :delete
timestamps()
end
def changeset(user, attrs) do user |> cast(attrs, [:name]) |> cast_embed(:billing_address) |> cast_embed(:shipping_addresses) end end
Virtual Fields
defmodule MyApp.Accounts.User do use Ecto.Schema
schema "users" do field :email, :string field :password_hash, :string
# Virtual fields - not persisted
field :password, :string, virtual: true
field :password_confirmation, :string, virtual: true
field :full_name, :string, virtual: true
timestamps()
end
def changeset(user, attrs) do user |> cast(attrs, [:email, :password, :password_confirmation]) |> validate_required([:email, :password]) |> validate_confirmation(:password) |> hash_password() end
defp hash_password(changeset) do case get_change(changeset, :password) do nil -> changeset password -> put_change(changeset, :password_hash, Bcrypt.hash_pwd_salt(password)) end end end
When to Use This Skill
Use ecto-schemas when you need to:
-
Define database table structures
-
Model relationships between entities
-
Create embedded documents
-
Handle complex data types
-
Implement virtual computed fields
Best Practices
-
Use meaningful schema module names
-
Keep schemas focused on data structure
-
Use embedded schemas for nested JSON
-
Define changesets in the schema module
-
Use Ecto.Enum for constrained values
-
Add database constraints with unique_constraint
Common Pitfalls
-
Forgetting timestamps() in schemas
-
Not handling on_replace for embeds
-
Mixing business logic into schemas
-
Forgetting to add associations on both sides
-
Not using virtual fields for sensitive data
-
Overusing embedded schemas when separate tables are better