ecto-schemas
Use when defining and working with Ecto schemas including field types, associations, and embedded schemas. Use when modeling database entities in Elixir.
When & Why to Use This Skill
This Claude skill provides comprehensive expertise in defining and managing Ecto schemas for Elixir applications. It streamlines the process of database entity modeling by offering precise guidance on field types, relational associations (one-to-one, one-to-many, many-to-many), and embedded schemas for complex JSON data. By leveraging this skill, developers can ensure data integrity through robust changeset implementations and optimize their Elixir backend architecture with best practices for database constraints and virtual fields.
Use Cases
- Database Entity Modeling: Rapidly define Elixir modules that map to database tables with accurate field types, including Enums, UUIDs, and custom binary data.
- Relational Mapping: Architect complex relationships between entities using belongs_to, has_many, and many_to_many macros to facilitate seamless data preloading and querying.
- Data Validation and Integrity: Create sophisticated changesets to validate user input, handle unique constraints, and ensure data consistency before persisting to the database.
- NoSQL-style Document Storage: Implement embedded schemas to manage nested JSON structures within relational databases, ideal for flexible metadata or configuration storage.
- Sensitive Data Handling: Utilize virtual fields within schemas to process sensitive information like plain-text passwords or computed properties without persisting them to the database.
| name | ecto-schemas |
|---|---|
| description | Use when defining and working with Ecto schemas including field types, associations, and embedded schemas. Use when modeling database entities in Elixir. |
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