--- title: "Phoenix: Ecto models" category: Elixir deprecated: true tags: [Archived] archived: This is for Phoenix 1.2 and below. [Phoenix 1.3 has a new API.](./phoenix-ecto@1.3). --- ### About This is for Phoenix 1.2 and below. [Phoenix 1.3 has a new API.](./phoenix-ecto@1.3). ### Generating ``` $ mix phoenix.gen.html Profile profiles email:string age:integer $ mix phoenix.gen.html User users email:string hashed_password:string ``` ### Schema ```elixir defmodule User do use Ecto.Schema schema "users" do field :name field :age, :integer # :id :binary :integer :float :boolean :string :binary # {:array, inner_type} :decimal :map field :password, virtual: true end end ``` ### Changesets ```elixir def changeset(user, params \\ :empty) do %User{} |> Ecto.Changeset.change # basic casting to changeset user |> cast(params, ~w(name email), ~w(age)) # params to Changeset |> validate_format(:email, ~r/@/) |> validate_inclusion(:age, 18..100) |> validate_exclusion(:role, ~w(admin superadmin)) |> validate_subset(:pets, ~w(cat dog parrot whale)) |> validate_length(:body, min: 1) |> validate_length(:body, min: 1, max: 160) |> validate_length(:partners, is: 2) |> validate_number(:pi, greater_than: 3) |> validate_number(:pi, less_than: 4) |> validate_number(:pi, equal_to: 42) |> validate_change(:title, fn _, _ -> []) |> validate_confirmation(:password, message: "does not match") |> unique_constraint(:email) |> foreign_key_constraint(:post_id) |> assoc_constraint(:post) # ensure post_id exists |> no_assoc_constraint(:post) # negative (useful for deletions) end ``` ```elixir changeset.valid? changeset.errors #=> [title: "empty"] changeset.changes #=> %{} changeset.params[:title] changeset.required #=> [:title] changeset.optional #=> [:body] ``` ### Updating ```elixir changeset #(or model) |> change(title: "New title") |> change(%{ title: "New title" }) |> put_change(:title, "New title") |> force_change(:title, "New title") |> update_change(:title, &(&1 <> "...")) |> delete_change(:title) |> merge(other_changeset) |> add_error(:title, "empty") ``` ### Getting ```elixir get_change(changeset, :title) #=> "hi" (if changed) get_field(changeset, :title) #=> "hi" (even if unchanged) fetch_change(changeset, :title) #=> {:ok, "hi"} | :error fetch_field(changeset, :title) #=> {:changes | :model, "value"} | :error ``` ## Ecto ### Get one ```elixir Repo.get(User, id) Repo.get_by(User, email: "john@hello.com") #=> %User{} | nil # also get! get_by! ``` ### Create/update ```elixir changeset |> Repo.update changeset |> Repo.insert changeset |> Repo.insert_or_update ``` ``` User |> Ecto.Changeset.change(%{name: "hi"}) |> Repo.insert ``` ## Many ### Queries ```elixir from p in Post, where: p.title == "Hello", where: [state: "Sweden"], limit: 1, offset: 10, order_by: c.name, order_by: [c.name, c.title], order_by: [asc: c.name, desc: c.title], preload: [:comments], preload: [comments: {c, likes: l}], join: c in assoc(c, :comments), join: p in Post, on: c.post_id == p.id, group_by: p, select: p, select: {p.title, p.description}, select: [p.title, p.description], ``` ### Get many ```elixir Repo.all(User) ``` ### Update many ```elixir Repo.update_all(Post, set: [title: "Title"]) Repo.update_all(Post, inc: [views: 1]) ``` ### Chaining `_all` with queries ```elixir from(p in Post, where: p.id < 10) |> Repo.update_all(...) from(p in Post, where: p.id < 10) |> Repo.all() ```