Database wrapper and query generator for elixir.
Ecto.Repo — where the data isEcto.Schema — what the data isEcto.Query — how to read the dataEcto.Changeset — how to change the datamix ecto.gen.repo -r Friends.Repo
config/config.exsconfig :friends, Friends.Repo,
database: "friends",
username: "user",
password: "pass",
hostname: "localhost"
config :friends, ecto_repos: [Friends.Repo] # Define repo to use in ecto
lib/friends/repo.exdefmodule Friends.Repo do
use Ecto.Repo,
otp_app: :friends,
adapter: Ecto.Adapters.Postgres
end
Friends.Repo in the app supervision tree in lib/friends/application.exmix ecto.createmix ecto.gen.migration create_peoplechange method in priv/repo/migrations/{generated_create_people_migration_file}create table(:people) do
add :first_name, :string
add :last_name, :string
add :age, :integer
end
mix ecto.migratedefmodule Friends.Person do
use Ecto.Schema
schema "people" do
field :first_name, :string
field :last_name, :string
field :age, :integer
end
end
person = %Friends.Person{}person = %{person | age: 28}person.age{:ok, person} = Friends.Repo.insert personFriends.Repo.insert changesetEcto.Changeset.cast(person, params, [:first_name, :last_name, :age])Ecto.Changeset.validate_required([:first_name])changeset.valid?changeset.errors[first_name: {"can't be blank", [validation: :required]},
last_name: {"can't be blank", [validation: :required]},
bio: {"should be at least %{count} character(s)", [count: 15, validation: :length, kind: :min, type: :string]}]
Ecto.Changeset.traverse_errors/2traverse_errors(changeset, fn {msg, opts} ->
Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(acc, "%{#{key}}", to_string(value))
end)
end)
%{
first_name: ["can't be blank"],
last_name: ["can't be blank"],
bio: ["should be at least 15 character(s)"],
}
from/2# `in` expression
from(c in City, select: c)
# Ecto.Queryable
from(City, limit: 1)
# Fragment with user-defined function and predefined columns
from(f in fragment("my_table_valued_function(arg)"), select: f.x)
# Fragment with built-in function and undefined columns
from(f in fragment("select generate_series(?::integer, ?::integer) as x", ^0, ^10), select: f.x)
^def with_minimum(age, height_ft) do
from u in "users",
where: u.age > type(^age, :integer) and u.height > ^(height_ft * 3.28),
select: u.name
end
with_minimum(18, 5.0)
from u in User, where: is_nil(u.age)
from u in (from u in User, where: u.age > 18), select: u.name
Single record
Repo.get(Movie, 1)Repo.get(Movie, title: "Ready Player One")Movie |> Ecto.Query.first() |> Repo.one()Movie |> Ecto.Query.last() |> Repo.one()Repo.get!(Movie, 1)Multiple records
Movie |> Repo.all()Movie |> Repo.stream() |> Enum.each(fn r -> ... end)Movie |> Repo.exists?()Keyword-based queries
query =
from Movie,
where: [title: "Ready Player One"],
select: [:title, :tagline]
Repo.all(query)
query =
from m in Movie,
where: m.title == "Ready Player One"
select: [m.title, m.tagline]
Repo.all(query)
Interpolation with ^
title = "Ready Player One"
query =
from m in Movie,
where: m.title == ^title,
select: [m.title, m.tagline]
Repo.all(query)
Pipe-based queries
Movie
|> where([m], m.title == "Ready Player One")
|> select([m], {m.title, m.tagline})
|> Repo.all
Single record
%Person{name: "Bob"}
|> Repo.insert()
params = %{"name"=>"Bob"}
%Person{}
|> Ecto.Changeset.cast(params, [:name])
|> Repo.insert()
Multiple records
data = [%{name: "Bob"}, %{name: "Alice"}]
Repo.insert_all(Person, data)
Single record
person =
Person
|> Ecto.Query.first()
|> Repo.one!()
changeset = change(person, %{age: 29})
Repo.update(changeset)
params = %{"age" => "29"}
person =
Person
|> Ecto.Query.First()
|> Repo.one!()
changeset = cast(person, params, [:age])
Repo.update(changeset)
Multiple records
Repo.update_all(Person, set: [age: 29])
Single record
person = Repo.get!(Person, 1)
Repo.delete(person)
Multiple records
Repo.delete_all(Person)