RUBY ON RAILS: BULK CREATION USING MULTIPLE SELECT CHECK BOX TAG

By: Saurav

2017-12-03 08:03:00 UTC

Recently I was working on a project where I had to allow users to choose multiple options at once and it should create relevant resources. Specifically, a repair shop owner was choosing what particular brands he provides services for. I call it brands we service as shown in the figure below.

Checkboxtag

As shown above, the owner selects multiple options from the checkbox and they all create resource brands_we_service. I was having some problems with multiple check boxes beforehand but finally I understand it and wanted to share.

Lets start with migration file:

class CreateBrandsWeServices < ActiveRecord::Migration
  def change
    create_table :brands_we_services do |t|
      t.string :title
      t.references :repairshop, index: true, foreign_key: true
      t.timestamps null: false
    end
  end
end

Each brands_we_service has a title and belongs to a repairshop. Each repairshop belongs to a user.

class Repairshop < ActiveRecord::Base
	belongs_to :user
	has_many :brands_we_services, :dependent => :destroy
end

class BrandsWeService < ActiveRecord::Base
	belongs_to :repairshop
	validates_uniqueness_of :title
end

For the routes, I am allowing only new, edit, create, update and destroy options for the user

  resources :repairshops do 
    resources :brands_we_services, only: [ :new, :edit, :create, :update, :destroy]
end

The form looks like:

<%= bootstrap_form_for [@repairshop, @brand_we_service] do |f| %>
  <% if @brand_we_service.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@brand_we_service.errors.count, "error") %> prohibited this coupon from being saved:</h2>

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

  <div class = "col-xs-12 col-sm-12 col-md-12 col-lg-12">
    <%= label_tag '','CHOOSE THE BRAND YOU SERVICE FROM THE LIST'  %>
  </div>

  
  
  <div class = "col-xs-12 col-sm-12 col-md-12 col-lg-12">

    <% ['Acura', 'Geo', 'Mini', 'Alfa Romeo', 'GMC', 'Mitsubishi', 'Am General','Honda','Nissan','American Motors', 'Hummer','Oldsmobile','Aston Martin','Hyundai', 'Opel','Audi','Infiniti','Panoz','Austin','International','Passport','Avanti','Isuzu','Peugeot','Bentley',  'Jaguar','Plymouth','Bertone','Jeep','Pontiac','BMW','Jensen','Porsche','Buick','Kia','Renault','Cadillac','Laforza','Rolls Royce','Checker','Lamborghini','Rover','Chevrolet','Lancia','Saab','Chrysler'  ,'Land Rover','Saturn','Citroen','Lexus','Shelby','Daewoo','Lincoln','Sterling','Daihatsu','Lotus','Subaru','Delorean','Maserati','Suzuki','Dodge','Mazda','Toyota','Eagle','Mercedes-Benz','Triumph','Ferrari','Mercury','Volkswagen','Fiat','Merkur','Volvo','Ford','MG','Yugo','General Motors'].each do |specialization| %>

      <div class = "col-xs-6 col-sm-6 col-md-6 col-lg-6 text-left">

        <%= check_box_tag 'specializations[]', specialization -%>
        <%= specialization -%>
      </div>  
    <% end %>
  
  </div>

  

<div class = "col-xs-12 col-sm-12 col-md-12 col-lg-12" style = "margin-top: 20px;">

  <%= f.text_field :title, :label => "IF NOT IN THE LIST ABOVE, ENTER THE BRAND" %>
  
    <%= f.submit "SUBMIT", class: "btn btn-danger" %>    
  </div>
  
<% end %>

And finally, the controller looks like:

class BrandsWeServicesController < InheritedResources::Base

	before_action :authenticate_user!, only: [:new, :create, :destroy, :update]
	before_action :is_user?, only: [:new, :create, :destroy, :update]

	before_action :set_parents, only:  [:new, :create] 
	before_action :set_child_and_parents, only:  [:destroy, :update, :update, :edit]


	def new
		@brand_we_service = BrandsWeService.new
	end

	def edit
				
	end

	def create

		if params[:specializations]

			params[:specializations].each do |brands_we_service_params|
				@brands_we_service = BrandsWeService.new({:title => brands_we_service_params, :repairshop_id => @repairshop.id})					
				
				if !BrandsWeService.find_by_title(brands_we_service_params)
					begin
						@brands_we_service.save!
					rescue
						p "brands not saved"
					end
				end				
			end

			respond_to do |format|
				format.html {redirect_to @repairshop, notice: 'brands were successfully created.'}
			end

		else

			@brands_we_service = BrandsWeService.new(brands_we_service_params)	
			@brands_we_service.repairshop_id = @repairshop.id

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

	end

	def destroy
		@brand_we_service.destroy
		respond_to do |format|						
		    format.html { redirect_to @repairshop, notice: 'brand was successfully destroyed.' }
		    format.json { head :no_content }
		end

	end

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

	private

	 def brands_we_service_params
      params.require(:brands_we_service).permit(:title)
    end

    def set_child_and_parents
    	@brand_we_service = BrandsWeService.find_by_id(params[:id])
    	@repairshop = Repairshop.find_by_id(params[:repairshop_id])
    end

    def set_parents    	
    	@repairshop = Repairshop.find_by_id(params[:repairshop_id])
    	p "hello #{@repairshop}"
    end

    def is_user?
		@repairshop = Repairshop.find(params[:repairshop_id])
		if @repairshop.user != current_user 
			redirect_to @repairshop, alert: "Sorry, You are not the author"
		end
	end
end

Let me explain.

1. When we are clicking the new button to create a brand we service, its talking us to the form.
2. The form has multiple fields and all the check box fields. Remember to use check_box_tag instead of check_box.
3. I am also passing the name of the tag in an array and adding it alongside the checkbox.
4. Remember to pass an array like specializations[] as the name, so the id would be specializations_ and the value would be the Model name. otherwise if we use name as specializations, the values won't be passed when you post the params to the controller and only one value would be chosen to be passed. A great Railscast tutorial which helped me out understand this concept is here: Ryan Bates Railscast

5. In the controller, I am checking if the params[:specializations] is present. If so, I am iterating through the array, using a bulk creation approach and creating each resource one by one after checking if its not been added.
6. If the params are not present, I am using a simple approach to create one brand.
7. I am using the approach in bullet 6 to allow users to create one resource at a time.

I will work on white listing params as well.

Nonetheless, for multiple check box bulk creation, this is the approach I would be taking and would like to hear your point of view. The final outcome as showcase on the page is added in the end.


Let me know what you think!
twitter: sprakash24oct
linkedin

Brandweservice

Owned & Maintained by Saurav Prakash

If you like what you see, you can help me cover server costs or buy me a cup of coffee though donation :)