如何在Phoenix框架中处理关联和嵌套表单?

时间:2015-08-17 15:47:56

标签: elixir phoenix-framework ecto

在Phoenix框架中处理关联和嵌套表单的方法是什么?如何创建具有嵌套属性的表单?如何在控制器和模型中处理它?

2 个答案:

答案 0 :(得分:15)

有一个处理1-1情况的简单例子。

想象一下,我们有一个Car和一个Engine模型,显然是Car has_one Engine。所以有汽车模型的代码

defmodule MyApp.Car do
  use MyApp.Web, :model

  schema "cars" do
    field :name, :string            

    has_one :engine, MyApp.Engine

    timestamps
  end

  def changeset(model, params \\ :empty) do
    model
    |> cast(params, ~w(name), ~w())
    |> validate_length(:name, min: 5, message: "No way it's that short")    
  end

end

和引擎模型

defmodule MyApp.Engine do
  use MyApp.Web, :model

  schema "engines" do
    field :type, :string            

    belongs_to :car, MyApp.Car

    timestamps
  end

  def changeset(model, params \\ :empty) do
    model
    |> cast(params, ~w(type), ~w())
    |> validate_length(:type, max: 10, message: "No way it's that long")    
  end

end

表单的简单模板 - >

<%= form_for @changeset, cars_path(@conn, :create), fn c -> %>

  <%= text_input c, :name %>

  <%= inputs_for c, :engine, fn e -> %>

    <%= text_input e, :type %>

  <% end %>  

  <button name="button" type="submit">Create</button>

<% end %>

和控制器 - &gt;

defmodule MyApp.CarController do
  use MyApp.Web, :controller
  alias MyApp.Car
  alias MyApp.Engine

  plug :scrub_params, "car" when action in [:create]

  def new(conn, _params) do    
    changeset = Car.changeset(%Car{engine: %Engine{}})    
    render conn, "new.html", changeset: changeset
  end

  def create(conn, %{"car" => car_params}) do    
    engine_changeset = Engine.changeset(%Engine{}, car_params["engine"])
    car_changeset = Car.changeset(%Car{engine: engine_changeset}, car_params)
    if car_changeset.valid? do
      Repo.transaction fn ->
        car = Repo.insert!(car_changeset)
        engine = Ecto.Model.build(car, :engine)
        Repo.insert!(engine)
      end
      redirect conn, to: main_page_path(conn, :index)
    else
      render conn, "new.html", changeset: car_changeset
    end
  end    

end

和一篇关于这个主题的有趣博客文章也可以澄清一些事情 - &gt; here

答案 1 :(得分:2)

has_many关系陷入同样的​​问题。遗憾的是,Car不能包含多个Engines,因此我会在TodoList TodoItems中使用相同的示例,TodoList <{1}}

defmodule MyApp.TodoList do use MyApp.Web, :model schema "todo_lists" do field :title, :string has_many :todo_items, MyApp.TodoItem timestamps end def changeset(model, params \\ :{}) do model |> cast(params, [:title]) |> cast_assoc(:todo_items) end end 型号:

TodoItem

defmodule MyApp.TodoItem do use MyApp.Web, :model schema "todo_items" do field :body, :string belongs_to :todo_list, MyApp.TodoList timestamps end def changeset(model, params \\ :{}) do model |> cast(params, [:body]) end end 型号:

TodoList

以下是表单创建<%= form_for @changeset, todo_lists_path(@conn, :create), fn f -> %> <%= text_input f, :title %> <%= inputs_for f, :todo_items, fn i -> %> <%= text_input i, :body %> <% end %> <button name="button" type="submit">Create</button> <% end %> 。为了简单起见,我们暂时只添加一个项目。

TodoListController

这就是create的样子。 defmodule MyApp.TodoListController do use MyApp.Web, :controller alias MyApp.TodoList alias MyApp.TodoItem def new(conn, _params) do todo_item = TodoItem.changeset(%TodoItem{}) changeset = TodoList.changeset(%TodoList{todo_items: [todo_item]}) render conn, "new.html", changeset: changeset end def create(conn, %{"todo_list" => todo_list_params}) do todo_item_changeset = TodoItem.changeset(%TodoItem{}, todo_item["todo_items"]["0"]) changeset = TodoList.changeset(%TodoList{}, %{title: todo_list_params["title"]}) |> Ecto.Changeset.put_assoc(:todo_items, [todo_item_changeset]) case Repo.insert(changeset) do {:ok, company} -> conn |> put_flash(:info, "TodoList created!") |> redirect(to: page_path(conn, :index)) {:error, changeset} -> conn |> render "new.html", changeset: changeset end end end 方法是最难对付的方法。我不得不深入研究Ecto Tests以找到一种方法来完成这项工作。 blogpost

{{1}}

Link