首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Phoenix框架中处理关联和嵌套表单?

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

Stack Overflow用户
提问于 2015-08-17 23:47:56
回答 2查看 7.9K关注 0票数 22

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

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-08-17 23:48:15

这里有一个处理1-1情况的简单示例。

假设我们有一个Car和一个Engine模型,显然还有一个Car has_one Engine。所以这里有汽车模型的代码

代码语言:javascript
复制
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

和引擎模型

代码语言:javascript
复制
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

表单->的简单模板

代码语言:javascript
复制
<%= 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 %>

和控制器->

代码语言:javascript
复制
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

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

票数 17
EN

Stack Overflow用户

发布于 2016-12-21 19:47:35

has_many关系中遇到了同样的问题。不幸的是,一个Car不能有多个Engines,所以我在这个blogpost中举了同样的例子,一个TodoList有很多个TodoItems

TodoList型号:

代码语言:javascript
复制
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型号:

代码语言:javascript
复制
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的过程。为了简单起见,我们现在只添加一项。

代码语言:javascript
复制
<%= 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方法是最难处理的。我不得不深入研究Ecto测试,以找到一种方法来实现这一点。Link

代码语言:javascript
复制
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
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32054561

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档