Rails attributes not being assigned properly - ruby-on-rails

I have a CRUD application where you can create pizzas. You can choose the base, the size and the toppings with select elements. For this I used collection_select.
However, when a pizza is created, these attributes are not assigned to the new object.
This is my form:
<div class="field">
<%= f.label :size_id %><br>
<%= collection_select(:size, :pizza_id, Size.all, :id, :name) %>
</div>
<div class="field">
<%= f.label :crust_id %><br>
<%= collection_select(:crust, :pizza_id, Crust.all, :id, :name) %>
</div>
<div class="field">
<%= f.label :toppings %><br>
<%= collection_select(:toppings, :pizza_id, Topping.all, :id, :name, {}, { :multiple => true }) %>
</div>
The controller, generated with scaffold:
class PizzasController < ApplicationController
before_action :set_pizza, only: [:show, :edit, :update, :destroy]
...
def create
#pizza = Pizza.new(pizza_params)
...
end
...
private
def pizza_params
params.require(:pizza).permit(:name, :price, :size, :crust, :topping => [])
end
end
When I look at the parameters in the log, I can see this:
Processing by PizzasController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"5oiP9EzpTC1fOVGSxv6MXF1qg786Vy3BtnIzEmCh19s=", "pizza"=>{"name"=>"Small pizza", "price"=>"10"}, "crust"=>{"pizza_id"=>"1"}, "toppings"=>{"pizza_id"=>["", "1", "2"]}, "commit"=>"Create Pizza"}
It looks like the attributes that are not being saved are not included in the pizza key, that's why they are not being assigned to the object.
I noticed the format of the input's name should be pizza[attribute] so it goes to the correct hash. So I tried something like this:
<%= collection_select(:size, :pizza_id, Size.all, :id, :name) %>
These are the parameters:
Processing by PizzasController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"5oiP9EzpTC1fOVGSxv6MXF1qg786Vy3BtnIzEmCh19s=", "pizza"=>{"name"=>"Small pizza", "price"=>"10", "size"=>"1"}, "crust"=>{"pizza_id"=>"1"}, "toppings"=>{"pizza_id"=>["", "1", "2"]}, "commit"=>"Create Pizza"}
It doesn't work because it is not transforming the size into the proper class.
What can I do?

you're bit incorrect with params order. It should look like
<div class="field">
<%= f.label :size_id %><br>
<%= collection_select(:pizza, :size_id, Size.all, :id, :name) %>
</div>
<div class="field">
<%= f.label :crust_id %><br>
<%= collection_select(:pizza, :crust_id, Crust.all, :id, :name) %>
</div>
<div class="field">
<%= f.label :toppings %><br>
<%= collection_select(:pizza, :topping_ids, Topping.all, :id, :name, {}, { :multiple => true }) %>
</div>
first parameter is hash root and then attribute
and change params method to be
def pizza_params
params.require(:pizza).permit(:name, :price, :size_id, :crust_id, :topping_ids)
end

I understand that you want all the keys included in the pizza key. This is what you need to do:
<%= collection_select(:pizza, :crust_id, Crust.all, :id, :name) %>
<%= collection_select(:pizza, :topping_ids, Topping.all, :id, :name, {}, { :multiple => true }) %>
If you look at the documentation for collection_select, it states that the first parameter is the key(which is pizza in your case) and the next parameter is the attribute name in the select box(crust/toppings)

Related

How to set custom field name ?

I am trying to use a collection_select for state just to filter cities, but I don't want to save the state_id in db. My "college" model has only city and college field. That's why it's throwing while instantiating :state_id I believe, I am new to rails. I am not able to figure it out. :(
<%= form_for #college do |f| %>
<div class="form-group">
<%= f.label "Select city"%>
<%= f.collection_select(:state_id, State.all, :id, :state, {}, {class: "form-control"})%>
</div><br>
<div class="form-group">
<%= f.label :city_id, "Select city"%>
<%= f.collection_select(:city_id, City.all, :id, :city, {}, {class: "form-control"})%>
</div><br>
<div class="form-group">
<%= f.label :college, "college Name"%>
<%= f.text_field :college, class: "form-control", placeholder: "Enter city name", required: true%>
<br>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary"%>
</div>
<% end %>
just define state_id attribute accessor in your college.rb model :
attr_accessor :state_id
inside your model you should define state_id as attr_accessor like :-
class College
attr_accessor :state_id
end

Rails controller saving from different table

I'm new to rails to bear with me.
This concerns two of my models: Product and Manufacturer.
When creating a new product the user can select which manufacturer the product belongs to from a drop down list. The problem is that I can't get this manufacturer to save.
I know I have to add some code to the controller and I've tried various things but to no avail.
Here's the view:
<h1>New Product</h1>
<%= form_for(#product) do |f| %>
<div>
<%= f.label :name, 'Name' %>
<%= f.text_field :name %>
</div>
<div>
<%= f.label :market_price, 'Market Price' %>
<%= f.text_field :market_price %>
</div>
<div>
<%= f.label :sell_price, 'Sell Price' %>
<%= f.text_field :market_price %>
</div>
<div>
<%= f.label :stock_level, 'Stock Level' %>
<%= f.text_field :stock_level %>
</div>
<div>
<%= f.label :manufacturer, 'Manufacturer' %>
<%= f.collection_select(:manufacturer, Manufacturer.all, :id, :name, prompt: true) %>
</div>
<div>
<%= f.label :location, 'Location' %>
<%= f.collection_select(:location, Product.all, :id, :location, prompt: true) %>
</div>
<br> </br>
<div>
<%= f.submit "Create Product" %>
</div>
And here's part of the controller:
def create
#product = Product.new(params[:product].permit(:name, :market_price, :sell_price, :stock_level, :location))
#product.save
flash[:notice] = 'Product Created'
redirect_to #product
end
end
After hours of trying several nesting methods, I still can't get this to work.
Surely it's very common to save fields from various models on one page???
You would usually nest your routes, so that products were within manufacturer:
resources :manufacturer do
resources :products
end
Then your form would be a form for an array:
form_for([#manufacturer, #product]) do |f|
f.hidden_field :manufacturer_id, value: #manufacturer.id
This allows you to pass in the ID of both the manufacturer and the product.
Now in your controller you can use something like the following, provided the associations are set up, such as product belongs_to :manufacturer and manufacturer has_many :products
#manufacturer = find(params[:manufacturer_id])
#product = #manufacturer.products.create()
In your form, you need to have a field for manufacturer_id, not manufacturer. You would change f.collection_select(:manufacturer to be f.collection_select(:manufacturer_id.
Then, in your controller, you need to add manufacturer_id to the list of parameters you are permitting in your permit method call. So it would be Product.new(params[:product].permit(:name, :market_price, :sell_price, :stock_level, :location, :manufacturer_id)).

Saving to an enum field in Rails 4.1.1

I am trying to make use of the enum feature that has been added to Rails. I had been waiting for this for quite some time.
Here is how I set it up:
Product model:
enum category: [:t_shirt, :hoodie, :jacket]
Product controller:
def create
#product = Product.new(product_params)
if #product.save
redirect_to #product, notice: 'Product was successfully created.' }
else
render :new
end
end
def product_params
params.require(:product).permit(:title, :description, :category, :price)
end
New form
<%= form_for(#product) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.text_field :price %>
</div>
<div class="field">
<%= f.label :category %><br>
<%= f.select :category, Product.categories, include_blank: "Select a category" %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
This correctly populates the drop-down field in my form with the values of the different enum options which I have defined in the model.
However, when I submit the form having selected one of the categories from the drop-down, it gives me an error:
'0' is not a valid category
Even though my category field is an integer field and '0' is the correct integer associated with the category I selected in my form.
It also highlights the following line from the create method in my Product controller as the place where the error occured:
#product = Product.new(product_params)
I am completely confused as to why this is happening. Would really appreciate some help.
Thank you.
Instead:
<%= f.select :category, Product.categories, include_blank: "Select a category" %>
Try:
<%= f.select :category, Product.categories.keys, include_blank: "Select a category" %>
Explain:
In Product.categories hash {"t_shirt"=>0, "hoodie"=>1, "jacket"=>2} but in Product.categories.keys array what you need ["t_shirt", "hoodie", "jacket"] for select helper.

Populating collection_select field on selection of another collection_select

I have two collection_select field in my form. One is to select category and the other is to select subcategory. I want the subcategory field to populate as per the category gets selected. I someone select category 1 then the subcategories belong to category 1 will be populated in the second collection_select field. I'm not sure what to do. What modifications I need in the following code I tried.
<div class="field">
<%= f.label :category_id %><br>
<%= f.collection_select(:category_id, Category.find(:all), :id, :name) %>
</div>
<div class="field">
<%= f.label :subcategory_id %><br>
<%= f.collection_select(:subcategory_id, Subcategory.where(category_id: :category_id), :id, :name) %>
</div>
You need to ajax to get this functionality
Try this Example:
form.html.erb
<script>
$(document).ready(fnction(){
$('#category_id').change(function(){
var value = $(this).val()
$.ajax({
type: 'POST',
url: 'options',
data: {id: value},
success: function(data){
$('#subcategory').html(data)
}
})
})
})
</script>
<div class="field">
<%= f.label :category_id %><br>
<%= f.collection_select(:category_id, Category.find(:all), :id, :name) %>
</div>
<div id="subcategory"></div>
controller:
def options
id = params[:id]
#subcategory = Subcategory.where(:category_id => id)
render :partial => 'options'
end
_options.html.erb:
<%= f.label :subcategory_id %><br>
change this line
<%= f.collection_select(:subcategory_id, #subcategory, :id, :name) %>
to
<%= f.input :subcategory_id, #subcategory %>
and finally in Routes.rb
post '/options' => 'categories#options'
You have two ways:
Use jquery. Something like that -
$(select_category).on('change', function(){ update_subcategory_options() })
where update_subcategory_options() - ajax request or another code.
Or use collection select with group (http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/option_groups_from_collection_for_select)

Submit button points to wrong action

I am building a form to work with a non activerecord model. I have two problems..
When I point to keyword/id/edit my form's submit button says 'create keyword', and pressing that submit button takes me to the create method on my keyword controller. It should be taking me to the update action.
I would also like to send an attribute called 'id' to my update action. I'm not sure how to do this. Like I said this isn't an activerecord model.
/views/keywords/_form.html.erb
<%= form_for(#keyword) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :keyword %><br />
<%= f.text_field :keyword %>
</div>
<div class="field">
<%= f.label :message1 %><br />
<%= f.text_area :message1 %>
</div>
<div class="field">
<%= f.label :message2 %><br />
<%= f.text_area :message2 %>
</div>
<div class="field">
<%= f.label :start_time %><br />
<%= f.text_area :start_time %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
/controller/keywords_controller.rb >> edit action
def edit
result = Keyword.find(params[:id])
result = result.to_array(:get_response, :return, :data, :item)
result = result.first
#keyword = Keyword.new
#keyword.id = result[:item][0][:value]
#keyword.name = result[:item][1][:value]
#keyword.keyword = result[:item][2][:value]
#keyword.message1 = result[:item][3][:value]
if(result[:item][4][:value] != {:"#xsi:type"=>"xsd:string"})
#keyword.message2 = result[:item][4][:value]
end
#keyword.start_time = result[:item][5][:value]
end
/models/keyword.rb
class Keyword
extend ActiveModel::Naming
include ActiveModel::Conversion
def persisted?
false
end
attr_accessor :id, :name, :keyword, :message1, :message2, :start_time
def self.get_keywords(cid)
#get data from webservice
end
end
config/routes.rb
SchoolBeacon::Application.routes.draw do
devise_for :users
devise_for :admins
resources :keywords
end
Change your #keyword = Keyword.new line in your edit action to #keyword = Keyword.find(params[:id]) or however you find the keyword. If you are using normal RESTful rails routes, that should do it.
Actually, you can just do form_for(#keyword, :url => keyword_path(#result) and change your result to an ivar(result becomes #result)

Resources