get_by etimologia elixir phoenix-framework ecto

elixir - etimologia - ¿Cómo manejar asociaciones y formas anidadas en el marco de Phoenix?



phoenix mix routes (2)

¿Cuál es la manera de manejar asociaciones y formas anidadas en el marco de Phoenix? ¿Cómo se crearía un formulario con atributos anidados? ¿Cómo se manejaría uno en el controlador y el modelo?


Hay un ejemplo simple de manejar la situación 1-1.

Imagina que tenemos modelos de Car y Engine y, obviamente, un Engine tiene un Engine único. Así que hay un código para el modelo de coche.

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

y el modelo de motor

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

Plantilla simple para el formulario ->

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

y el controlador ->

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

y una interesante publicación de blog sobre el tema que también puede aclarar algunas cosas -> here


Se encontró con el mismo problema con una relación has_many . Desafortunadamente, un Car no puede tener muchos Engines , así que tomaría el mismo ejemplo en este here , de un TodoList , con muchos TodoItems

Modelo de TodoList :

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

Modelo de 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

Aquí está la creación de la forma un TodoList . Para mantener las cosas simples, solo agreguemos un artículo por ahora.

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

Así es como se vería TodoListController . El método de create fue el más difícil de acertar. Tuve que indagar en Pruebas Ecto para encontrar una manera de hacer que esto funcionara. 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/