Search code examples
elixirphoenix-frameworkecto

How to toggle update model bool attribute?


How to toggle active bool attribute in update action? I'm used to Rails, what is good practice in Phoenix?

Example code:

defmodule Todo.task do
  use Todo.Web, :model

  schema "task" do
    field :active, :boolean, default: false

    timestamps
  end

  @required_fields ~w(active)
  @optional_fields ~w()

  def changeset(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
  end
end

defmodule Todo.TaskController do
  use Todo.Web, :controller
  alias Todo.Task

  def update(conn, %{"id" => id}) do
    task = Repo.get_by(Task, id: id)
    # task.active = !task.active
    # task.save
    render(conn, "show.json", task: task)
  end
end

Solution

  • You can use an Ecto.Changeset with your new active state and then call Repo.update/2.

      def update(conn, %{"id" => id}) do
        task = Repo.get_by(Task, id: id)
        changeset = Task.changeset(task,%{active: !task.active})
        case Repo.update(changeset) do
          {:ok, task}         -> redirect(conn, to: task_path(conn, :show, task))
          {:error, changeset} -> render(conn, "edit.html", changeset: changeset)
        end
      end
    

    The pattern match when calling Repo.update or Repo.insert is the considered best practice for what you want to do.

    Often you will call update with the params that you have pattern matched in the function:

      def update(conn, %{"id" => id, "task" => task_params}) do
        changeset = Task.changeset(task, task_params)
        case ...
      end
    

    The changeset/2 function defined on your model will ensure that only the fields specified can be modified. If these differ from the fields when updating, consider making an update_changeset/2 function. There is nothing special about this function, you can define and use as many functions as you like that return a changeset.