This is Devhints.io cheatsheets — a collection of cheatsheets I've written.

# Phoenix: Ecto models

This is for Phoenix 1.2 and below. Phoenix 1.3 has a new API..

## Generating

    $ mix phoenix.gen.html Profile profiles email:string age:integer
    $ mix phoenix.gen.html User users email:string hashed_password:string

## Schema

    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

    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

    changeset.valid?
    changeset.errors     #=> [title: "empty"]

    changeset.changes    #=> %{}
    changeset.params[:title]

    changeset.required   #=> [:title]
    changeset.optional   #=> [:body]

### Updating

    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

    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

    Repo.get(User, id)
    Repo.get_by(User, email: "john@hello.com")  #=> %User{} | nil

    # also get! get_by!

### Create/update

    changeset |> Repo.update
    changeset |> Repo.insert
    changeset |> Repo.insert_or_update

    User
    |> Ecto.Changeset.change(%{name: "hi"})
    |> Repo.insert

## Many

### Queries

    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

    Repo.all(User)

### Update many

    Repo.update_all(Post, set: [title: "Title"])
    Repo.update_all(Post, inc: [views: 1])

### Chaining `_all` with queries

    from(p in Post, where: p.id < 10)
    |> Repo.update_all(...)

    from(p in Post, where: p.id < 10)
    |> Repo.all()

Devhints.io cheatsheets is a collection of cheatsheets I've written over the years. Suggestions and corrections? Send them in. I'm Rico Sta. Cruz. Check out my Today I learned blog for more.