Reputation: 1530
So I have three tables: locations, items, and ingredients, that have the following relationships between each other:
Location
has many Items
.Item
belongs to a Location
and has many Ingredients
.Ingredient
belongs to an Item
.When viewing an item (at show.html.haml), I want to be able to add in ingredients that it contains right there on the page, while using AJAX remote: true
for the form. However, once I get to the create
action of the IngredientsController
, I am unable to do this:
@ingredient = item.build_ingredient(:name => ingredient_params[:name], etc..)
Point is, the controller keeps throwing NoMethodError
for build_ingredient
, create_ingredient
, create
, etc. Nothing works. But the relationship between Location
and Items
works perfectly fine.
The weird thing for me is, though, that this works:
@ingredient = Ingredient.new
@ingredient.build_item(hashes for an Item object here, etc..)
I am still quite a bit new to Rails and I practice every day. Normally I would eventually come across a solution online through Google, but this one has me particularly stumped and after reading the Rails documentation on associations, I have still not come across anything that directly addresses a kind of "3-Layered" association between models.
So here's what I have:
Ingredients Controller
class IngredientsController < ApplicationController
def create
# This works
location = Location.find_by(:location_id => ingredient_params[:id])
# This also works
item = location.items.where(:item_id => ingredient_params[:menu_item_id])
# This does not work
@ingredient = item.build_ingredient(:name => ingredient_params[:name], :unit => ingredient_params[:unit], :value => ingredient_params[:value])
respond_to do |format|
format.js { render nothing: true }
end
end
private def ingredient_params
params.require(:ingredient).permit(:id, :menu_item_id, :name, :unit, :value)
end
end
show.html.haml (Items)
.panel.panel-default
= form_for @ingredient, remote: true do |f|
.panel-heading
New Ingredient
.panel-body
= f.hidden_field :id, :value => params[:id]
= f.hidden_field :menu_item_id, :value => params[:menu_item_id]
= f.select :name, options_from_collection_for_select(Supplylist.all, "id", "item"), { :prompt => "Select..." }, :class => "form-control ingredient-select"
%br
= f.text_field :value, :class => "form-control"
%br
= f.select :unit, options_from_collection_for_select(UnitType.all, "id", "name"), { :prompt => "Select..." }, :class => "form-control ingredient-select"
.panel-footer
= f.submit "Add", :class => "btn btn-success"
Location Model
class Location < ActiveRecord::Base
has_many :items
@url
def getResponse
response = RestClient.get(
@url,
{:content_type => :json, :'Api-Key' => '000000000000000000000'})
return response
end
def setURL(url)
@url = url
end
def getJSON
return getResponse()
end
def getData(url)
self.setURL(url)
return JSON.parse(getJSON())
end
end
Item Model
class Item < ActiveRecord::Base
belongs_to :location
has_many :ingredients
end
Ingredients Model
class Ingredient < ActiveRecord::Base
belongs_to :item
end
Also, just to note:
items
table in my database DOES have a location_id
foreign key column.ingredients
table in my database has an item_id
foreign key column.Both columns were created during model creation using location:belongs_to
and item:belongs_to
.
Upvotes: 1
Views: 87
Reputation: 24337
First of all item
does not contain a single item. Change your item query to:
item = location.items.where(:item_id => ingredient_params[:menu_item_id]).first
Then you can use:
item.ingredients.build(:name => ingredient_params[:name], etc..)
build_<association_name>
is only valid for belongs_to or has_one associations.
Upvotes: 1