Должна существовать ошибка при сохранении вложенных атрибутов через многие ко многим

Jan 21 2021

Попытка сохранить вложенные записи через ассоциацию многие-ко-многим в Rails 6, но получаю ошибку «тег должен существовать». Тег является родительским для post_tags, который представляет собой таблицу перекрестных ссылок между сообщениями и тегами (многие-ко-многим). Что я хочу сделать, так это при создании нового сообщения сохранить записи post_tag, связанные с выбранными тегами в форме сообщения. Я просмотрел несколько связанных сообщений: здесь и здесь , и попытался использовать inverse_of, autosave: true и optional: true, но они, похоже, не работают.

Вот что у меня есть:

Модели

class Post < ApplicationRecord
  has_many :post_tags, dependent: :destroy, inverse_of: :post, autosave: true
  has_many :tags, through: :post_tags
end

class PostTag < ApplicationRecord
  belongs_to :post
  belongs_to :tag
end

class Tag < ApplicationRecord
  has_many :post_tags, dependent: :destroy, inverse_of: :tag, autosave: true
  has_many :posts, through: :post_tags
end

Контоллер

PostsController < ApplicationController
  def new
    @post = Post.new
    @tags= Tag.all
    @post.post_tags.build
  end

  def create
    @post = Post.new(post_params)
    @post.post_tags.build
    
    if @post.save
      ...
    end
  end
  
  private

  def post_params
        params.require(:post).permit(:title, :content, :user_id, post_tags_attributes: [tag_id: []])
  end
end

Форма

<%= f.fields_for :post_tags do |builder| %>
    <%= builder.collection_check_boxes :tag_id, Tag.top_used, :id, :name, include_hidden: false %>
<% end %>

Ошибка

   (0.4ms)  ROLLBACK
  ↳ app/controllers/posts_controller.rb:229:in `create'
Completed 422 Unprocessable Entity in 41ms (ActiveRecord: 3.7ms | Allocations: 15178)


  
ActiveRecord::RecordInvalid (Validation failed: Post tags tag must exist):

Ответы

1 max Jan 21 2021 at 23:36

Вам не нужно явно создавать экземпляры «модели соединения». Вам просто нужно передать массив tag_ids=сеттеру, созданному has_many :tags, through: :post_tags.

<%= form_with(model: @post) %>
  ...
  <div class="field">
    <%= f.label :tag_ids %>
    <%= f.collection_check_boxes :tag_ids, @tags, :id, :name %>
  </div>
  ...
<% end %>

Ваш контроллер должен выглядеть так:

PostsController < ApplicationController
  def new
    @post = Post.new
    @tags = Tag.all
  end

  def create
    @post = Post.new(post_params)
    if @post.save
      redirect_to @post, status: :created
    else
      @tags = Tag.all
      render :new
    end
  end
  
  private

  def post_params
        params.require(:post)
              .permit(:title, :content, :user_id, tag_ids: [])
  end
end

Использование вложенных атрибутов и fields_for для создания экземпляров модели соединения действительно необходимо только в том случае, если вам нужно сохранить дополнительную информацию в модели соединения.