Search code examples
ruby-on-railsnested-formsnested-attributes

Nested forms not storing data into database in rails 4


I have a vendor model, a product model, and a vendor_product model. In my vendors form, I have used nested form to create vendor_products with attributes as vendor_id, product_id and copies.On creating a new vendor, it also creates a vendor_product. But for some reason, it does not stores the vendor_id and product_id in vendor_products table but only stores the copies

My associations are as follows

A vendor ->

        has_many :vendor_products
        has_many :products, through: :vendor_products

A product ->

        has_many :vendor_products
        has_many :vendors, through: :vendor_products

A vendor_product

        belongs_to :vendor
        belongs_to :product

Vendor.rb

class Vendor < ActiveRecord::Base

  has_many :vendor_products
  has_many :products, through: :vendor_products
  accepts_nested_attributes_for :vendor_products, :products, 
 :allow_destroy => true

end

My vendors/_form.html.erb

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

  <ul>
  <% @vendor.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.fields_for :vendor_products do |vproducts| %>
  <div class="field">
  <%= vproducts.label :product %><br>
    <%= collection_select(:product, :product_ids, Product.all, :id,
    :product_name,
             {:prompt => 'Please select', :multiple => true }) %>
  </div>
  <div class="field">
    <%= vproducts.label :copies %><br>
    <%= vproducts.number_field :copies %>
  </div>
  <% end %>

 <div class="actions">
  <%= f.submit %>
</div>
<% end %>

My vendors_controller.rb

class VendorsController < ApplicationController
      before_action :set_vendor, only: [:show, :edit, :update, :destroy]
      respond_to :json

      def index
        @vendors = Vendor.all.limit(20)
      end


      def show 
      end 


      def new 
        @vendor = Vendor.new
        @vendor.products.build
        @vendor.vendor_products.build
      end


      def edit
      end


      def create
        @vendor = Vendor.new(vendor_params)      

        respond_to do |format|
         if @vendor.save
            format.html { redirect_to @vendor, notice: 'Vendor was   successfully 
     created.' }
           format.json { render :show, status: :created, location: @vendor    }
         else
           format.html { render :new }
           format.json { render json: @vendor.errors, status: 
           :unprocessable_entity }
         end
       end
      end


     def update
        respond_to do |format|
        if @vendor.update(vendor_params)
           format.html { redirect_to @vendor, notice: 'Vendor was successfully 
      updated.' }
           format.json { render :show, status: :ok, location: @vendor }
        else
          format.html { render :edit }
          format.json { render json: @vendor.errors, status: 
     :unprocessable_entity }
        end
      end
    end



      private

      def set_vendor
        @vendor = Vendor.find(params[:id])
      end

      def vendor_params
         params.require(:vendor).permit(:name, :email, :phone_no, :addressline1, 
      :addressline2, :landmark,
      :city, :state, :country, :pincode, :latitude, :longitude, :status, 
        product_attributes: [:product_id, :product_name, :price ], 
        vendor_products: [:vendor_product_id, :vendor_id, :product_id, 
        :copies])
      end
    end

Now a vendor and VendorProduct is created but my vendor_product looks lik this

    {"id":3,
     "vendor_id":null,
     "product_id":null,
     "copies":4,
    }

Can any one point out how to fix this. What am I doing wrong. Please bear in mind that I am a rails newbie.


Solution

  • change once vendor_products to vendor_products_attributes and look
    

    In your view

    <%= collection_select(:product, :product_ids, Product.all, :id,
        :product_name,
                 {:prompt => 'Please select', :multiple => true }) %>
    
    replace with 
    
    <%= vproducts.select :product_id, options_from_collection_for_select(Product.all, "id", "name"), prompt: "Select something" %>