2015-08-17 8 views

Antwort

15

Es gibt ein einfaches Beispiel zur Handhabung der 1-1-Situation.

Stellen haben wir ein Car und ein Engine Modelle und natürlich ein Car has_one Engine. So gibt es Code für das Automodell

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 

und das Motormodell

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 

Einfache Vorlage für die Form ->

<%= 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 %> 

und die Steuerung ->

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 

und ein interessanter Blogbeitrag auf das Thema, das einige Dinge auch klären kann ->here

+0

Das Beispiel einige kleinere Fehler haben kann, da ich keine Chance auf Eigentum Test hatte es oder nicht exakt richtige Art und Weise geschrieben werden, da ich zu diesem Rahmen neu bin mir aber ich Ich hoffe, es hilft jemandem, ein wenig Zeit zu sparen, um diese Dinge für sich selbst herauszufinden. Nur versuchen, die Suche nach Antworten auf einige Fragen zu erleichtern, da es nicht viele Informationen zum Thema gibt. – JustMichael

+0

Das Problem mit dieser Lösung, wenn zum Beispiel "Car" eine Einschränkung wie die Eindeutigkeit für seinen Namen hätte, würde die Transaktion fehlschlagen und kein Fehler würde für das Formular angegeben werden. Ich stecke darin fest: http://stackoverflow.com/a/38035026?noredirect=1 – masylum

+0

Hier ist ein Test, der zeigt, wie Sie die verschachtelte Einfügung in Ecto 2 tun würden, https://github.com/elixir-ecto/ ecto/blob/6f1971f4120b84e1a441792feb77ba451c4fc783/integration_test/cases/repo.exs # L720 – opsb

2

Ran in das gleiche Problem mit einer has_many Beziehung. Leider haben ein Car nicht viele Engines, also würde ich das gleiche Beispiel in diesen blogpost nehmen, ein TodoList, mit vielen TodoItems

TodoList Modell:

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 Modell:

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 

Hier ist die Formularerstellung TodoList. Um die Dinge einfach zu halten, fügen wir einfach einen Artikel hinzu.

<%= 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 %> 

Dies ist, wie TodoListController aussehen würde. Die create Methode war die schwierigste, um richtig zu machen. Ich musste mich in Ecto Tests vertiefen, um einen Weg zu finden, wie das funktioniert. Link

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 

http://pranavsingh.co/storing-nested-associations-with-phoenix-forms/

+2

Haben Sie eine Idee, wie Sie mehrere Elemente hinzufügen und sie dynamisch in Form hinzufügen/entfernen? –