Search code examples
ruby-on-railsformsruby-on-rails-4modelsmodel-associations

Call create action through a form with a variable from another controller


Two of the models I have in the rails 4 app are the following:

class Council < ActiveRecord::Base
    has_many :alternatives
    ...
end

class Alternative < ActiveRecord::Base
    belongs_to :council
    ...
end

I am rendering an Alternative form that allows me to create a new Alternative object from a Council's show view:

councils/show.html.erb

<%= render 'alternatives/form' %>

alternatives/_form.html.erb

<%= form_for(@alternative) do |f| %>
  <div class="form-group">
      <div>
        <%= f.text_field :title, :placeholder => 'Provide your alternative', autofocus: true, class:"form-control" %>
      </div>
      <div>
        <%= f.text_area :more_info, :placeholder => 'Describe your alternative', autofocus: true, class:"form-control", rows: '4' %>
      </div>
  </div>
  <div>
      <%= f.submit 'Submit the alternative!', class:"btn btn-success"  %>
  </div>
<% end %>

At that point, I want to associate the Alternative object with the specific Council object from the show view, like the code below, but the variable @council is not defined:

controllers/alternatives_controller.rb

class AlternativesController < ApplicationController
  before_action :set_alternative, only: [:show, :edit, :update, :destroy]

  def create
    @alternative = Alternative.new(alternative_params)
    @alternative.council = @council
  end

  private

    def set_alternative
      @alternative = Alternative.find(params[:id])
    end

    def alternative_params
      params.require(:alternative).permit(:title, :more_info)
    end
end

That will allow me to show all the alternatives associated with a certain Council object:

councils/show.html.erb

...
<% @council.alternatives.each do |alternative| %>
    <%= alternative.title %>
    <%= alternative.more_info %>
<% end %>
...

I've carefully reviewed the Ruby on Rails Guides (http://guides.rubyonrails.org/association_basics.html#belongs-to-association-reference) but clearly I am missing something. Any ideas? Thank you.


Solution

  • Several options for you:


    Nested

    I've not tested this, but you could use multiple objects in your form_for helper:

    #app/views/councils/show.html.erb
    <%= form_for [@council, @alternative] do |f| %>
     ...
    <% end %>
    

    This will send your request through to the council_alternatives_path (you can change it), and provide you with params[:council_id] and params[:id]

    You could set your routes like this to get it to work:

    #config/routes.rb
    resources :councils do
       resources :alternatives, only: [:create]
    end
    

    This will be a little hacky I think (as it's meant for forms with nested attributes), but it's still a viable way to achieve what you want


    Nested Attributes

    The other option is to use the accepts_nested_attributes_for directive in your model. This will be overkill for what you need I think, but could help:

    #app/models/council.rb
    Class Council < ActiveRecord::Base
       has_many :alternatives
       accepts_nested_attributes_for :alternatives
    end
    
    #app/models/alternative.rb
    Class Alternative < ActiveRecord::Base
       belongs_to :council
    end
    

    This will allow you to use the @council object to save any new Alternative objects you need:

    #app/controllers/councils_controller.rb
    Class CouncilsController < ApplicationController
       def show
          @council = Council.find params[:id]
          @council.alternatives.build
       end
    
       def update
          @council = Council.find params[:id]
          @council.update(council_params)
       end
    
       private
    
       def council_params
          params.require(:council).permit(alterantives_attributes: [:alternatives, :attributes])
       end
    end
    

    This will allow you to use the following form code:

    #app/views/councils/show.html.erb
    <%= form_for @council do |f| %>
       <%= f.fields_for :alternatives do |a| %>
          <%= a.text_field :your_info %>
       <% end %>
       <% f.submit %>
    <% end %>