如何使用 elixir/phoenix 从 csv 文件导入用户?

Posted

技术标签:

【中文标题】如何使用 elixir/phoenix 从 csv 文件导入用户?【英文标题】:How to import users from csv file with elixir/phoenix? 【发布时间】:2017-10-09 00:53:00 【问题描述】:

我有一个 csv 文件,其中包含用户列表和 UserController 中的以下导入方法,我想通过提交带有表单的 csv 文件来导入这些用户。看起来我做错了。

users_controller

  def import(conn, %"user" => user_params) do
    user_params["file"]
    |> File.stream!()
    |> CSV.decode
    |> Enum.each(fn(user) -> User.changeset(%User, %name: Enum.at(user, 0), email: Enum.at(user, 1)) |> Repo.insert end)
    conn
    |> put_flash(:info, "Imported")
    |> redirect(to: user_path(conn, :index))
  end

路线

post "/import", UsersController, :import, as: :import_csv

表格

<%= render "import_form.html", changeset: @changeset,
                        action: import_csv_path(@conn, :import) %>

-

<%= form_for @changeset, @action, [multipart: true], fn f -> %>
  <div class="form-group">
    <%= label f, :file, class: "control-label" %>
    <%= file_input f, :file %>
  </div>

  <div class="form-group">
    <%= submit "Submit", class: "btn btn-primary" %>
  </div>
<% end %>

型号

  schema "users" do
    field :name, :string
    field :email, :string
    field :file, :any, virtual: true
    timestamps()
  end

  def changeset(struct, params \\ %) do
    struct
    |> cast(params, [:name, :email, :file])
    |> validate_required([:name, :email])
    |> unique_constraint(:email)
  end

以下代码适用于 iex

  def import(file) do
    file
    |> File.stream!()
    |> CSV.decode
    |> Enum.each(fn(user) -> User.changeset(%User, %name: Enum.at(user, 0), email: Enum.at(user, 1)) |> Repo.insert end)
  end

【问题讨论】:

您是否遇到任何错误? 我收到了no function clause matching in IO.chardata_to_string/1,但下面的答案解决了这个问题。我需要选择path。 ty 【参考方案1】:

您的参数中没有返回文件。这是我为多部分文件返回的参数

[warn] module=UcxChat.AttachmentController line=10 function=create/2  attachment params: %"channel_id" => "300233dd-782f-4718-9eed-00b8cc412a79", "description" => "", "file" => %Plug.Uploadcontent_type: "text/plain", filename: "test.txt", path: "/var/folders/wt/3q11kty15rqfb5v9rpqg0ssm0000gn/T//plug-1494/multipart-421483-239132-3", "file_name" => "test.txt", "type" => "text/plain", "user_id" => "427452eb-c9cf-457b-9c55-0904c9d24385"

您可以看到params["file"] 是一个Plug.upload 结构。

要获取实际文件,您应该获取 :path 字段,例如:

  def import(conn, %"user" => user_params) do
    user_params["file"].path
    |> File.stream!()
    |> CSV.decode
    |> Enum.each(fn(user) -> User.changeset(%User, %name: Enum.at(user, 0), email: Enum.at(user, 1)) |> Repo.insert end)
    conn
    |> put_flash(:info, "Imported")
    |> redirect(to: user_path(conn, :index))
  end

您可能还想进行一些错误处理。比如:

def import(conn, %"user" => user_params) do
  user_params["file"].path
  |> File.stream!()
  |> CSV.decode
  |> Enum.map(fn(user) -> 
    User.changeset(%User, %name: Enum.at(user, 0), email: Enum.at(user, 1)) |> Repo.insert 
  end)
  |> Enum.filter(fn 
    :error, cs -> true
    _ -> false
  end)
  |> case do
    [] -> 
      conn
      |> put_flash(:info, "Imported")
      |> redirect(to: user_path(conn, :index))
    errors -> 
      errors = parse_errors(errors)  # create this fun 
      conn
      |> put_flash(:erorr, errors)
      |> render("import.html")
  end
end

【讨论】:

如果您不介意,只是一个小问题。在这种情况下,我真的需要使用变更集吗?我记得我尝试使用form_for @conn, action 表单,它给了我assign @conn not available in eex template. 谢谢 不确定“在这种情况下我真的需要使用变更集”是什么意思吗?此外,如果您尝试从部分使用 @conn(从模板中调用 render),则需要传递 conn 绑定。 &lt;%= render "form.html", conn: @conn %&gt; 我的意思是不使用模型 对不起,如果我很密集,但我仍然没有关注。你能详细说明一下吗? 我已经发现了我的问题 :) 谢谢。现在我只使用&lt;%= form_for @conn, @action, [as: :import_csv, multipart: true], fn f -&gt; %&gt;def import(conn, %"import_csv" =&gt; file) do 所以我不向我的表单提供@changeset。

以上是关于如何使用 elixir/phoenix 从 csv 文件导入用户?的主要内容,如果未能解决你的问题,请参考以下文章

从 Phoenix / Elixir GET 函数中调用 Typescript 函数

GraphQL Elixir/Phoenix API:Socket 挂起,响应很大

sh Elixir Phoenix Cheatsheet

将 elixir phoenix 应用程序部署到 heroku - 无法添加 buildpack

如何在 Angular 2 应用程序中使用 Phoenix 通道/套接字?

如何在服务器上正确安装 Erlang、Elixir 和 mix?