Zagnieżdżone zestawy zmian nie są jeszcze obsługiwane w ecto: https://github.com/elixir-lang/ecto/issues/618 Musisz samodzielnie zapisywać tagi.
W poniższych fragmentach kodu wybiorę tag_ids
i wstawię je do tabeli sprzężenia, jeśli Post.changeset/2
da mi prawidłowy wynik. W celu zatrzymania wybranych znaczników w formularzu I dodano wirtualne pole, które możemy odczytać w formularzu i ustawić domyślne. To nie jest najlepsze rozwiązanie, ale działa dla mnie.
PostController
def create(conn, %{"post" => post_params}) do
post_changeset = Post.changeset(%Post{}, post_params)
if post_changeset.valid? do
post = Repo.insert!(post_changeset)
case Dict.fetch(post_params, "tag_ids") do
{:ok, tag_ids} ->
for tag_id <- tag_ids do
post_tag_changeset = PostTag.changeset(%PostTag{}, %{"tag_id" => tag_id, "post_id" => post.id})
Repo.insert(post_tag_changeset)
end
:error ->
# No tags selected
end
conn
|> put_flash(:info, "Success!")
|> redirect(to: post_path(conn, :new))
else
render(conn, "new.html", changeset: post_changeset)
end
end
PostModel
schema "posts" do
has_many :post_tags, Stackoverflow.PostTag
field :title, :string
field :tag_ids, {:array, :integer}, virtual: true
timestamps
end
@required_fields ["title"]
@optional_fields ["tag_ids"]
def changeset(model, params \\ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
end
PostTagModel (JoinTable do tworzenia wielu wielu związku)
schema "post_tags" do
belongs_to :post, Stackoverflow.Post
belongs_to :tag, Stackoverflow.Tag
timestamps
end
@required_fields ["post_id", "tag_id"]
@optional_fields []
def changeset(model, params \\ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
end
PostForm
<%= form_for @changeset, @action, fn f -> %>
<%= if f.errors != [] do %>
<div class="alert alert-danger">
<p>Oops, something went wrong! Please check the errors below:</p>
<ul>
<%= for {attr, message} <- f.errors do %>
<li><%= humanize(attr) %> <%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-group">
<%= label f, :title, "Title" %>
<%= text_input f, :title, class: "form-control" %>
</div>
<div class="form-group">
<%= label f, :tag_ids, "Tags" %>
<!-- Tags in this case are static, load available tags from controller in your case -->
<%= multiple_select f, :tag_ids, ["Tag 1": 1, "Tag 2": 2], value: (if @changeset.params, do: @changeset.params["tag_ids"], else: @changeset.model.tag_ids) %>
</div>
<div class="form-group">
<%= submit "Save", class: "btn btn-primary" %>
</div>
<% end %>
Jeśli chcesz zaktualizować tagi, masz dwie opcje.
- Usuń wszystkie kryteria i wstawić nowe wpisy
- Poszukaj zmian i zachować istniejące wpisy
Mam nadzieję, że to pomaga.
I Rozszerzyliśmy moich modeli opartych na przykładzie podanym tutaj, ale mam kilka pytań: W MyApp.Tag Ci napisać „#other pól post” Zakładam, że masz na myśli „#other pola TAG” W przyszłości w jaki sposób mogę na przykład zapytać o wszystkie posty z określonym tagiem lub wszystkie tagi dla określonego posta? Zakładam, że te przygotowane zapytania należy umieścić w modelu TagPost? Chyba pytam, czy istnieją jakieś "skróty" lub uproszczony sposób robienia tego? Czy będę musiał ręcznie napisać długie zapytanie? – Wobbley
@ Brofessor Jak zmieniłbyś PostController, aby odzwierciedlał zmiany, które sugerujesz? – helcim