Вложенные изменения еще не поддерживаются в ecto: https://github.com/elixir-lang/ecto/issues/618 Вы должны сохранить теги самостоятельно.
В следующих фрагментах кода я выберу tag_ids
и вставляю их в таблицу соединений, если Post.changeset/2
дает мне действительный результат. Для удержания выбранных тегов в Форме I добавлено виртуальное поле, которое мы можем прочитать в форме и установить значение по умолчанию. Это не лучшее решение, но оно работает для меня.
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 для создания многих многих ассоциаций)
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 %>
Если вы хотите обновить тег, у вас есть два варианта.
- Удалить все и вставить новые записи
- Посмотрите на изменения, и сохранить существующие записи
Я надеюсь, что это помогает.
Я расширил мои модели, основанные на вашем примере, приведенных здесь, но у меня есть несколько вопросов: В MyApp.Tag вы пишете «#Other почтовых полей», я полагаю, вы имеете в виду «#Other полех тегов» В будущем как я, например, запрошу все сообщения с определенным тегом или все теги для определенного сообщения? Я предполагаю, что эти подготовленные запросы должны быть помещены в модель TagPost? Я предполагаю, что я спрашиваю, есть ли какие-либо «ярлыки» или упрощенный способ сделать это? Или мне придется вручную написать длинный запрос? – Wobbley
@ The Brofessor Как бы вы изменили PostController, чтобы отразить изменения, которые вы предлагаете? – helcim