stavro/arc_ecto

How do I handle a nil value for Plug.Upload in params?

Opened this issue · 1 comments

  • Elixir 1.2
  • Phoenix 1.2.1
  • arc: 0.6.0
  • arc_ecto 0.5.0

According to the Phoenix documentation, if no file is selected, Plug.Upload will not be present in the params map.

Finally, notice that when there is no data from the file input, we get neither the "photo" key nor a Plug.Upload struct. Here are the user_params from the log.

So if you submit a blank form, params[:user] will be nil. For example:

<%= form_for @changeset, @action, [multipart: true], fn f -> %>
  <%= file_input f, :avatar %>
  <%= submit "Submit" %>
<% end %>

Notice the absence of the "user" key.

%{"_csrf_token" => "...", "_method" => "put", "_utf8" => "✓"}

Which raises an error in the controller.

bad request to App.Account.AvatarController.update, no matching action clause to process request
  def update(conn, %{"user" => user_params}, current_user) do
    changeset = User.avatar_changeset(current_user, user_params)
    case Repo.update(changeset) do
      {:ok, _} ->
        redirect(conn, to: current_user_avatar_path(conn, :edit))
      {:error, changeset} ->
        render(conn, :edit, changeset: changeset, user: current_user)
    end

We can hack around the error by including a hidden field in the form, forcing params[:user] to exist.

<%= hidden_input f, :id %>

But Plug.Upload will still be missing from the params. Which means the changeset will be valid. Notice changes being an empty map.

#Ecto.Changeset<action: nil, changes: %{}, errors: [], data: #App.User<>,
 valid?: true>

So, my questions are this:

  1. What's the point of |> validate_required([:avatar]) given that Plug.Upload is only present with file data? To reach this point in the changeset, a field must be present. It seems redundant then to validate for presence.
  2. How do we validate the presence of avatar?

Here's my user schema:

defmodule App.User do
  use App.Web, :model
  use Arc.Ecto.Schema

  schema "users" do
    # ...
    field :avatar, App.AvatarUploader.Type
  end

  def avatar_changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:avatar])
    |> cast_attachments(params, [:avatar])
    |> validate_required([:avatar])
  end

Am I missing something?

Maybe the quickiest way is to pattern match on the update function in controller where you check that params doesn't contain the "user" key.