Я не могу создавать объекты модели с помощью accept_nested_attributes_for. Он не создаст вложенный объект

Моя структура модели выглядит так:

Доска has_many тем. Тема has_many Сообщений.

app / models / board.rb

class Board < ActiveRecord::Base
    has_many :topics
end

app / models / topic.rb

class Topic < ActiveRecord::Base
    belongs_to :user
    belongs_to :board
    has_many :posts

    accepts_nested_attributes_for :posts

    validates :title, presence: true, length: { maximum: 255 }
    validates :user_id, presence: true
    validates :board_id, presence: true
    ...
end

app / models / post.rb

class Post < ActiveRecord::Base
    belongs_to :user
    belongs_to :topic

    validates :user_id, presence: true
    validates :topic_id, presence: true
    validates :content, length: { minimum: 8 }
end

Вот мой взгляд на создание новой темы. раздел fields_for используется для создания: content в новом сообщении

app / views / themes / new.html.erb

<div>
    <%= form_for [@board, @topic] do |f| %>

    <%= render 'shared/error_messages', object: @topic %>

    <%= f.label :title %>
    <%= f.text_field :title %>

    <%= f.fields_for @post do |p| %>
        <%= p.label :content %>
        <%= p.text_area :content %>
    <% end %>

    <%= f.submit "Post new topic", class: "button submit" %>
    <% end %>
</div>

При создании новой темы я хочу, чтобы также был создан новый пост с: content из формы. Поскольку сообщение зависит от наличия темы, чтобы быть действительным, их необходимо создавать или отклонять одновременно (если: content или: title недействительны). Мне сказали, что accept_nested_attributes_for будет работать правильно, но когда мой код выполняется, он создает только тему, а не сообщение.

приложение / контроллеры / themes_controller.rb

def new
    @board = Board.find(params[:board_id])
    @topic = @board.topics.build
    @post = @topic.posts.build
end

def create
    @board = Board.find(params[:board_id])
    @topic = @board.topics.build(topic_params.merge({user_id: current_user.id}))

    if @topic.save
        flash[:success] = "Topic created"
        redirect_to @topic
    else
        render 'new'
    end
end

private

def topic_params
    params.require(:topic).permit(:title, posts_attributes: [:content])
end

Для записи, вот мой контроллер сообщений и маршруты, если это поможет.

приложение / контроллеры / posts_controller.rb

def create
    @topic = Topic.find(params[:topic_id])
    @post = @topic.posts.build(post_params.merge({user_id: current_user.id}))

    if @post.save
        flash[:success] = "Post Created"
        redirect_to topic_path(@topic) 
    else
        render 'new'
    end
  end

  private

    def post_params
      params.require(:post).permit(:content)
    end

рейк-маршруты для досок, тем и сообщений

    topic_posts GET    /topics/:topic_id/posts(.:format)      posts#index
                POST   /topics/:topic_id/posts(.:format)      posts#create
 new_topic_post GET    /topics/:topic_id/posts/new(.:format)  posts#new
      edit_post GET    /posts/:id/edit(.:format)              posts#edit
           post GET    /posts/:id(.:format)                   posts#show
                PATCH  /posts/:id(.:format)                   posts#update
                PUT    /posts/:id(.:format)                   posts#update
                DELETE /posts/:id(.:format)                   posts#destroy
   board_topics GET    /boards/:board_id/topics(.:format)     topics#index
                POST   /boards/:board_id/topics(.:format)     topics#create
new_board_topic GET    /boards/:board_id/topics/new(.:format) topics#new
     edit_topic GET    /topics/:id/edit(.:format)             topics#edit
          topic GET    /topics/:id(.:format)                  topics#show
                PATCH  /topics/:id(.:format)                  topics#update
                PUT    /topics/:id(.:format)                  topics#update
                DELETE /topics/:id(.:format)                  topics#destroy
         boards GET    /boards(.:format)                      boards#index
                POST   /boards(.:format)                      boards#create
      new_board GET    /boards/new(.:format)                  boards#new
     edit_board GET    /boards/:id/edit(.:format)             boards#edit
          board GET    /boards/:id(.:format)                  boards#show
                PATCH  /boards/:id(.:format)                  boards#update
                PUT    /boards/:id(.:format)                  boards#update
                DELETE /boards/:id(.:format)                  boards#destroy

А также значение params в начале themes_controller # create

{"utf8" => "✓", "Authentity_token" => "...", "topic" => {"title" => "Новый заголовок", "post" => {"content" => "Новый Content "}}," commit "=>" Опубликовать новую тему "," action "=>" create "," controller "=>" themes "," board_id "=>" 1 "}


comment
Обратите внимание, что ваш params не включает post_attributes, как предлагает ваш post_params. Попробуйте вместо этого использовать f.fields_for @post, as: :post_attributes do |p|   -  person MrYoshiji    schedule 08.08.2014
comment
Я изменил поля fields_for, как было предложено, но мои параметры по-прежнему выглядят следующим образом: {utf8 = ›✓, Authentity_token =› ....., topic = ›{title =› NEW TITLE, post = ›{content =› NEW CONTENT}} , commit = ›Создать тему, action =› create, controller = ›themes, board_id =› 2}   -  person Adam    schedule 11.08.2014
comment
Хорошо. Итак, я изменил его на <%= f.fields_for :posts do |p| %>, и теперь мои параметры передаются с хешем posts_attributes. Но теперь у них возникают проблемы с проверкой модели Post. При отправке формы я получаю 2 ошибки формы: Posts user can't be blank и Posts topic can't be blank.   -  person Adam    schedule 12.08.2014
comment
Мне удалось исключить Posts user can't be blank, включив в форму скрытое поле, например: <%= p.hidden_field :user_id, :value => current_user.id %>. Я не думаю, что это хороший способ сделать это, но у меня нет лучших идей. У меня все еще есть Posts topic can't be blank. Я не могу понять, как это должно работать. Сообщение нуждается в topic_id для сохранения, но у темы не будет идентификатора, пока он не будет сохранен.   -  person Adam    schedule 13.08.2014


Ответы (1)


Наконец-то нашел решение здесь

Это было после того, как я исправил форму для правильного создания параметров.

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

topic.rb

class Topic < ActiveRecord::Base
  belongs_to :user
  belongs_to :board
  has_many :posts, :inverse_of => :topic

   accepts_nested_attributes_for :posts

  validates :title, presence: true, length: { maximum: 255 }
  validates :user, presence: true
  validates :board, presence: true
end

post.rb

class Post < ActiveRecord::Base
  belongs_to :user
  belongs_to :topic, :inverse_of => :posts

  validates :user, presence: true
  validates :topic, presence: true
  validates :content, presence: true
end

app / views / themes / new.html.erb

<div>
  <%= form_for [@board, @topic] do |f| %>
  <%= render 'shared/error_messages', object: @topic %>

  <%= f.label :title %>
  <%= f.text_field :title %>

  <%= f.fields_for :posts do |p| %>
      <!-- I needed to pass in the current_user.id for the post -->
      <%= p.hidden_field :user_id, :value => current_user.id %>

      <%= p.label :content %>
      <%= p.text_area :content %>
  <% end %>

  <%= f.submit "Post new topic", class: "button submit" %>
  <% end %>
</div>

приложение / контроллеры / themes_controller.rb

def create
  @board = Board.find(params[:board_id])
  @topic = @board.topics.build(topic_params.merge({user_id: current_user.id}))
  debugger

  if @topic.save
    flash[:success] = "Topic created"
    redirect_to @topic
  else
    render 'new'
  end
end

private

def topic_params
    params.require(:topic).permit(:title, posts_attributes: [:content, :user_id])
end
person Adam    schedule 12.08.2014
comment
Убедитесь, что user_id, переданный в параметрах, совпадает с идентификатором current_user (прямо сейчас я мог вручную изменить скрытый ввод в форме и установить его на другой, что привело бы к созданию сообщения, принадлежащего кому-то другому) - person MrYoshiji; 14.08.2014