2

My 3 models follow a linear structure. Board has many Topics. Topic has many Posts.

My models look like this:

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

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

    # Temporary holder for new topic content
    attr_accessor :content

  ...
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

app/views/topic/new.html.erb

<h1>Create new topic</h1>

Return to <%= link_to @board.title, @board %>

<div>
    <%= form_for(:topic) do |f| %>

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

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

    <%= f.fields_for(:posts) do |p| %>

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

    <% end %>

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

When I create a new Topic, part of the job of the topics_controller#create action is to also create a Post with the content provided by the user. Right now, what I end up doing is just deleting the Topic if the Post fails to save.

app/controllers/topics_controller.rb

def create
    @board = Board.find(params[:id])
    # Here, topic_params are just the strong parameters for Topic
    @topic = @board.topics.build(topic_params.merge({user_id: current_user.id}))
    # Blank post for the view
    @post = Post.new

    if @topic.save
        post_content = {content: params[:topic][:content], user_id: current_user.id}
        @post = @topic.posts.build(post_content)

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

    else
        render 'new'
    end
end

I can't think of any other way to do this without changing the validations on the model. For @post to be valid, it must have a topic_id. So @topic has to have been successfully saved. But if @topic is valid and saves but @post does not save, then I don't want the @topic to remain the the database. It would be preferable if @topic and @post are only ever both created or neither of them are.

Adam
  • 518
  • 7
  • 17
  • r u concerned abt only 2nd model? or if 3rd will be failed then you want to del 2nd too? – RAJ Aug 07 '14 at 19:22
  • If creating the Post (3rd model) fails, then I don't want to need to delete the Topic (2nd model). – Adam Aug 07 '14 at 23:40

1 Answers1

1

You should use rails accepts_nested_attributes. Set up your Models

class Board < ActiveRecord::Base
  has_many :topics
  ...
end

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

  accepts_nested_attributes_for :posts
  ...
end

Your form:

<%= form_for @topic do |f|  %>
  // topic fields
  <%= f.fields_for @post do |p| %>
    // post fields
  <% end %>
  <%= f.button :submit, :class => "button", value: 'Submit'  %>
<% end %>

Your controller

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

def create
  @board = Board.find(params[:id])
  @topic = @board.topics.build(topic_params)
  if @topic.save  # you are only checking for topic if you post is not saved then your topic will also wont save and you wont have to delete anything
    redirect_to @topic
  else
    render "new"
  end
end

private

def topic_params
  params.require(:topic).permit(:id, :attribute, posts_attributes: [:title])
end
Mandeep
  • 9,093
  • 2
  • 26
  • 36
  • 1
    This looks like exactly what I need. Clarification, should "def form_params" be "def topic_params" or are those different strong parameters? – Adam Aug 07 '14 at 21:36
  • @Nephelus yeah it'll be topic params. Updated my answer – Mandeep Aug 08 '14 at 00:11
  • I've detailed my full solution here: http://stackoverflow.com/questions/25211089/i-cant-create-model-objects-using-accepts-nested-attributes-for-it-wont-creat – Adam Aug 12 '14 at 22:08