0

I have a form for creating a new :thing, with a collection_select field to enter an existing :thing the new :thing is related to. Each :thing has_many :things, through an intermediary model :related_things, which has a thing_a_id and thing_b_id. So when I fill in the field and click submit, a :related_thing is supposed to be created with thing_a_id and thing_b_id equal to the two thing_ids, respectively. But no such :related_thing is created; the form doesn't do anything. The other textfields do work though. What's wrong with my code?

I'm using Rails 4.0.10.

Things/new View:

<h1>Add Something!</h1>
<p>
  <%= form_for @thing, :url => things_path, :html => { :multipart => true } do |f| %>

    <%= f.text_field :name, :placeholder => "Name of the thing" %>
    <br>
    <%= f.label :related_things %>
    <%= f.collection_select :related_things, Thing.all, :id, :name %>
    <br>
    <%= f.label :display_picture %>
    <%= f.file_field :avatar %>
    <br>
    <%= f.submit "Submit", class: "btn btn-primary" %>
  <% end %>
</p>

Thing Model:

class Thing < ActiveRecord::Base
  has_many :related_things
  has_many :things, :through => :related_things
  has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "30x30!" }, :default_url => "/images/:style/missing.png"
  validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/

  def related_things
    related_thing_ids = RelatedThing.
        where("thing_a_id = ? OR thing_b_id = ?", self.id, self.id).
        map { |r| [r.thing_a_id, r.thing_b_id] }.
        flatten - [self.id]
    Thing.where(id: related_thing_ids)
  end

  def related_thing_ids=(ids)
    ids.each do |id|
      record = RelatedThing.where(thing_a_id: self.id, thing_b_id: id).first
      record ||= RelatedThing.where(thing_a_id: id, thing_b_id: self.id).first
      record ||= RelatedThing.create!(thing_a_id: self.id, thing_b_id: id)
    end
  end

end

RelatedThing Model:

class RelatedThing < ActiveRecord::Base
  has_many :things
end

Things Controller:

class ThingsController < ApplicationController

  def show
    @thing = Thing.find(params[:id])
    @related_thing = RelatedThing.all
    @thing.things.build  
  end

  def new
    @thing = Thing.new
    @things = Thing.all
  end

  def create
    @thing = Thing.new(thing_params)
    if @thing.save
      redirect_to @thing
    else
      render 'new'
    end
  end

  private

    def thing_params
      params.require(:thing).permit(:name, :image_path, :avatar)
    end

end

RelatedThings Controller:

class RelatedThingsController < ApplicationController
  def new
    @things = Thing.all.by_name
  end

  def create
    @things = Thing.all.by_name
  end

  def edit
    @things = Thing.all.by_name
  end
end

2 Answers 2

1

There are two problems causing this:

  1. As Jamesuriah pointed out, your collection_select should use the related_things_ids field instead.

  2. Despite that change, the field is actually being filtered out of the parameter map because of Rails' Strong Parameters.

Specifically, in your controller, the thing_params method should look like:

def thing_params
  params.require(:thing).permit(:name, :image_path, :avatar, :related_things_ids)
end

Read up on strong parameters in the link above for more info. Hope that helps!

Sign up to request clarification or add additional context in comments.

Comments

1

The collection select should be named related_thing_ids for your model to work, I believe.

3 Comments

No, I'm getting the same result. The new Thing is created but without a related_thing.
@user3739453 double check sure you're permitting the param? Also double check and debug your related_thing_ids method
Ah, I wasn't permitting the param. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.