Search code examples
ruby-on-railsrubyforum

Insert data into multiple tables from one controller/view [Rails 4]


I have more curious questions for all you amazing people!

I am creating a forum and when you create a topic, you are also creating the first post at the same time.

I need to assign variables to certain fields.

Example: :user_id => current_user.id,

I don't have the param settings correct, so many of the fields are NULL when stored in the database.

Models

class Topic < ActiveRecord::Base
  belongs_to :forum
  has_many :posts, :dependent => :destroy  
  belongs_to :user
  accepts_nested_attributes_for :posts
end

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

Topics Controller

# GET /topics/new
def new
  @topic = Topic.new
  @topic.posts.build
end

def create  
  @topic = Topic.new(topic_params)

  if @topic.save 
    #@topic.responses = Post.new(params[:responses])
    flash[:success] = "Topic Posted"
    redirect_to "/forums/#{@topic.forum_id}" 
  else  
    render :new  
  end   
end

def topic_params
  # last_post_at = (:last_post_at => Time.now)
  params.require(:topic).permit(
    :name, 
    :description, 
    [:last_poster_id => current_user.id], 
    [:last_post_at => Time.now], 
    [:user_id => current_user.id],
    :forum_id,
    posts_attributes: [:id, :content, :topic_id, :user_id => current.user.id] )
end

Post Controller

# GET /posts/new
def new
  @post = Post.new
end

def create  
  @post = Post.new(
    :content => params[:post][:content], 
    :topic_id => params[:post][:topic_id], 
    :user_id => current_user.id)  

  if @post.save  
    @topic = Topic.find(@post.topic_id)  
    @topic.update_attributes(
      :last_poster_id => current_user.id, 
      :last_post_at => Time.now)  
    flash[:notice] = "Successfully created post."  
    redirect_to "/topics/#{@post.topic_id}"  
  else  
    render :action => 'new'  
  end  
end  

_form for View/Topic

<%= form_for(@topic) do |f| %>
  <% if params[:forum] %>
    <input type="hidden" 
    id="topic_forum_id" 
    name="topic[forum_id]" 
    value="<%= params[:forum] %>" />
  <% end %>  

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :description %><br>
    <%= f.text_field :description %>
  </div>

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

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

  <% end %>
  <%= f.submit :class => "btn btn-primary" %>
<% end %>

Solution

  • You'll likely be looking for a function called:

    accepts_nested_attributes_for
    

    You put this into the model you're working with (in your case Post) and it will pass paeans for the nested model through to the corresponding controller

    There is a good RailsCast about this and I've gr some experience with it too. If you want me to post working live code, let me know (I'm on my iPhone)


    Live Code

    Models

    #app/models/image_post.rb
    belongs_to :post, :class_name => 'Post'
    belongs_to :image, :class_name => 'Image'
    accepts_nested_attributes_for :image, :allow_destroy => true
    
    #app/models/post.rb
    has_many :images, -> { uniq }, :class_name => 'Image', :through => :images_posts, dependent: :destroy
    has_many :images_posts, :class_name => 'ImagePost'
    accepts_nested_attributes_for :images_posts, :allow_destroy => true
    

    Controller

        def new
                @post = Post.new
                @post.images_posts.build.build_image
        end
    
        def create
                #Using Inherited Resources Gem
                create! 
        end
    
        private
        def permitted_params
                {:post => params.require(:post).permit(:title, :body, images_posts_attributes: [:caption, image_attributes: [:image]] )}
        end
    

    Form

    <%= form_for [:admin, resource], :html => { :multipart => true }  do |f| %>
            <table class="resource_table">
                    <thead>
                            <th colspan="2"><%= params[:action].capitalize %> <%= resource_class %></th>
                    </thead>
                    <tbody class="form">
                            <% attributes.each do |attr| %>
                                    <tr class="<%= cycle('odd', '')%>">
                                            <td><%= resource_class.human_attribute_name(attr) %></td>
                                            <td>
                                                    <% if attr == "body" %>
                                                            <%= f.text_area attr, :rows => 60, :cols => 80, :class => "redactor" %>
                                                    <% else %>
                                                            <%= f.text_field attr, :value => resource.public_send(attr).to_s %>
                                                    <% end %>
                                            </td>
                                    </tr>
                            <% end %>
                            <%= f.fields_for :images_posts do |images_posts| %>
                                    <%= images_posts.fields_for :image do |images| %>
                                            <tr>
                                                    <td>Image</td>
                                                    <td><%= images.file_field :image %></td>
                                            </tr>
                                    <% end %>
                                    <tr>
                                            <td>Caption</td>
                                            <td><%= images_posts.text_field :caption %></td>
                                    </tr>
                            <% end %>
                            <tr class="dull">
                                    <td colspan="2"><%= f.submit "Go" %></td>
                            </tr>
                    </tbody>
            </table>
    <% end %>