Ecto Schemas
Master Ecto schema definitions to model your database entities effectively in Elixir applications.
Basic Schema Definition
elixirdefmodule 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
elixirschema "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
elixirdefmodule 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
elixirdefmodule 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
elixirdefmodule 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

