SamuraiBlue
SamuraiBlue

Reputation: 861

Rails: How to save the parent value of association

What I'd like to do is to save the shop_id in the Item model.

My models are as followings;

models

shop.rb

class Shop < ActiveRecord::Base
    belongs_to :order
    has_many :items
    has_many :categories
end

item.rb

class Item < ActiveRecord::Base
    belongs_to :shop
    has_many :categories
end

schema

ActiveRecord::Schema.define(version: 20160615060137) do

...

  create_table "shops", force: :cascade do |t|
    t.string   "name"
    t.integer  "order_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "items", force: :cascade do |t|
    t.string   "name"
    t.integer  "shop_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

...

end

How can I create controller new and create to save shop_id in items? I can get the parameter such as Shop.find_by(id: params[:shop_id])

It would be appreciated if you could give me any suggestion.

UPDATE

When I try @Saad's answer, NoMethodError was displayed as below.

NoMethodError (undefined method `to_key' for #<Item::ActiveRecord_Associations_CollectionProxy:0x007f901e079a30>
Did you mean?  to_query
               to_ary):
  app/views/items/new.html.erb:4:in `_app_views_items_new_html_erb__3601549985910054210_70128472136980'



NoMethodError (undefined method `to_key' for #<Item::ActiveRecord_Associations_CollectionProxy:0x007f901e079a30>
Did you mean?  to_query
               to_ary):
  app/views/items/new.html.erb:4:in `_app_views_items_new_html_erb__3601549985910054210_70128472136980'

The line of new.html.erb:4 is as following (I haven't use partial _form.html.erb so far);

  <%= form_for(@item, url: shop_items_path, method: :post) do |f| %>

UPDATE2

routes.rb is as below.

routes.rb

Rails.application.routes.draw do

  ...

  resources :items,              only: [:new, :create, :edit, :update]

  resources :shops do
    resources :items
  end

  ...

Upvotes: 0

Views: 128

Answers (2)

DennisCastro
DennisCastro

Reputation: 161

Improving the previuos code and assuming you shops and items controller are nested:

class ItemsController < ApplicationController
  before_action :load_shop

  def index
    @items = @shop.items
  end

  def new
    @item = Item.new
    @item.shop = @shop
  end

  def create
    @item = Item.new(item_params)
    respond_to do |format|
      if @item.save
        format.html { redirect_to shop_item_path(@shop, @item), notice: 'Item was successfully created.' }
        format.json { render :show, status: :created, location: @item }
      else
        format.html { render :new }
        format.json { render json: @item.errors, status: :unprocessable_entity }
      end
    end
  end

  private
  def load_shop
    @shop = Shop.find(params[:shop_id])
  end

  def item_params
    params.require(:item).permit(:name, :shop_id)
  end
end


<%= form_for([@shop, @item]) do |f| %>

    <div class="field">
      <%= f.label :name %><br>
      <%= f.text_field :name %>
    </div>
    <%= f.hidden_field :shop_id%>
    <div class="actions">
       <%= f.submit %>
    </div>
<% end %>

Upvotes: 1

Saad
Saad

Reputation: 1904

items_controller.rb

class ItemsController < ApplicationController
  before_action :set_item, only: [:show, :edit, :update, :destroy]

  def index
    @shop = Shop.find(params[:shop_id])
    @items = @shop.items
  end

  def new
    @shop = Shop.find(params[:shop_id])
    @item = Item.new
  end
    def create
    @shop = Shop.find(params[:shop_id])
    @item = Item.new(item_params)

    respond_to do |format|
      if @item.save
        format.html { redirect_to shop_item_path(@shop, @item), notice: 'Item was successfully created.' }
        format.json { render :show, status: :created, location: @item }
      else
        format.html { render :new }
        format.json { render json: @item.errors, status: :unprocessable_entity }
      end
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_item
      @shop = Shop.find params[:shop_id]
      @item = @shop.items.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def item_params
      params.require(:item).permit(:name, :shop_id)
    end
end

_form.html.erb

<%= form_for(@item, url: shop_items_path, method: :post) do |f| %>
  <% if @item.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@item.errors.count, "error") %> prohibited this item from being saved:</h2>

      <ul>
      <% @item.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
  <%= f.hidden_field :shop_id, value: @shop.id %>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

index.html.erb

<p id="notice"><%= notice %></p>

<p>
  <strong>Name:</strong>
  <%= @item.name %>
</p>

<p>
  <strong>Shop:</strong>
  <%= @item.shop %>
</p>

<%= link_to 'Edit', edit_shop_item_path(@shop, @item) %> |
<%= link_to 'Back', shop_items_path(@shop) %>

Upvotes: 0

Related Questions