Wie können im Phoenix-Framework Assoziationen und verschachtelte Formulare behandelt werden? Wie würde man ein Formular mit verschachtelten Attributen erstellen? Wie würde man es in der Steuerung und im Modell handhaben?Wie mit Zuordnungen und verschachtelten Formularen in Phoenix-Framework umzugehen?
Antwort
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
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/
Haben Sie eine Idee, wie Sie mehrere Elemente hinzufügen und sie dynamisch in Form hinzufügen/entfernen? –
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
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
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