Problems Using collection_select to store value in the database - ruby-on-rails

I have two models:
Project.rb
class Project < ActiveRecord::Base
belongs_to :customer
end
and Customer.rb
class Customer < ActiveRecord::Base
has_many :projects
end
Inside the _form.html.erb I have:
<p>
<label>Select Customer</label>
<%= f.collection_select :customer_id, Customer.all, :id, :name, :include_blank => true %>
</p>
Which should Collect the Customers from the Customer model and display all the customers, finally it should assign the value to the customer_id which is in projects table.
Rite now the everything is passing when i check the log. When I select the first customer with value=1, it passes customer_id = "1" in my log but it doesn't get stored in the table. It shows customer_id = nil in the projects table.
Can someone help. Thanks :)

Do check that you added customer_id in attr_accessible method like,
class Project
attr_accessible :your_other_attributes, :customer_id
end

Related

Unpermitted parameter for join table via multiple select in Rails

I'm running into an error when nesting parameters in Rails 5: Unpermitted parameter: specialties
I have an Expertise model:
class Expertise < ApplicationRecord
has_many :buckets, through: :specialties
has_many :specialties
end
A Bucket model:
class Bucket < ApplicationRecord
has_many :expertises, through: :specialties
has_many :specialties
end
And a Specialty model:
class Specialty < ApplicationRecord
belongs_to :expertise
belongs_to :bucket
end
I'm trying to allow the User to edit his or her Expertises and adjust the Specialties associated with them. The #buckets are passed in from the controller, and the form currently looks like this:
<%= form_for(expertise) do |f| %>
<%= f.fields_for :specialties do |s| %>
<%= s.collection_select :bucket_ids, #buckets, :id, :name, {}, { multiple: true, class: "input" } %>
<% end %>
<% end %>
I based the form on this answer.
Here's the relevant snippet from the ExpertisesController:
def expertise_params
params.require(:expertise).permit(:user_id, :name, :rating, :description, specialties_attributes: [:id, :expertise_id, :bucket_id, :_destroy, bucket_ids: []])
end
And here are the parameters that are being passed in:
Parameters: {"expertise"=>{"specialties"=>{"bucket_ids"=>["", "1"]}, "description"=>""}, "id"=>"97"}
Specialties should be an array, right? I'm not sure how to do that.
The aim is to easily enable the User to select from the available Buckets (#buckets) to toggle his or her Expertise Specialties on or off. So let's say there are 5 Buckets available, the User would only be able to toggle on/off 5 possible Specialties for that Expertise.
Unpermitted parameter: specialties
You didn't set up accept_nested_attributes_for which spits out with that error
class Expertise < ApplicationRecord
has_many :specialties
has_many :buckets, through: :specialties
accepts_nested_attributes_for :specialties
end
When I try that, the nested fields_for form doesn't return any
specialties and so the HTML element is empty. Then, when I try to use
#expertise.specialties.build, I get undefined method bucket_ids for
Specialty because bucket_ids isn't actually an attribute, but
bucket_id is. Worth keeping in mind that the User needs to be able to
toggle multiple Specialties, each of which is tied to a Bucket (via a
bucket_id), and from what I've ready I'm supposed to use bucket_ids
(the plural) there
You don't need to have plural form(_ids) just because to accept multiple values. Just keep bucket_id to accept multiple values. And don't forget to build the associated model in the controller
def new
#expertise = Expertise.new
#expertise.specialties.build
end
Change bucket_ids to bucket_id in the form
<%= s.collection_select :bucket_id, #buckets, :id, :name, {}, { multiple: true, class: "input" } %>
And finally, expertise_params should be
def expertise_params
params.require(:expertise).permit(:user_id, :name, :rating, :description, specialties_attributes: [:id, :expertise_id, :_destroy, bucket_id: []])
end
Update:
Ok after some research, it looks like it should be bucket_ids, but the bucket_ids should be allowed as attribute for expertise. Check this post and tweak your form and expertise_params accordingly. You won't be needing accept_nested_attributes_for too!
The situation: Expertise has_many Buckets through Specialties and you want to update some bucket status of a specific expertise. So you can do this:
class ExpertisesController < ApplicationController
def your_action
#expertise = Expertise.find params[:id]
bucket_ids = params[:expertise][:specialties][:bucket_ids]
#expertise.specialties.where(id: bucket_ids).update(status: :on)
end
end

How can I synchronize a has_many association using accepts_nested_attributes_for by foreign_key instead of ID?

I would like to synchronize a has_many association by foreign key. It seems I have to write custom code to do this. Is there any Rails / Active Record magic / Gem to achieve this? Specifically, I'd like to synchronize a join-table where the pairs of foreign keys should be unique.
class Food < ActiveRecord::Base
has_many :food_tags, :dependent=>:destroy, :inverse_of => :food
accepts_nested_attributes_for :food_tags, :allow_destroy => true
end
class FoodTag < ActiveRecord::Base
belongs_to :tag, :inverse_of=>:food_tags
belongs_to :food, :inverse_of=>:food_tags
end
class Tag < ActiveRecord::Base
has_many :food_tags, :dependent=>:destroy, :inverse_of=>:tag
has_many :foods, :through=>:food_tags
end
For my form with nested attributes (or my JSON API), I'd really like to omit the FoodTag id and use the tag_id to synchronize when updating a food.
I want to submit this on update to show that the tag is set or cleared
# this one is set
food[food_tag_attributes][0][tag_id] = 2114
food[food_tag_attributes][0][_destroy] = false
# this one is cleared
food[food_tag_attributes][1][tag_id] = 2116
food[food_tag_attributes][1][_destroy] = true
Instead, I have to submit this for update:
# this one is set
food[food_tag_attributes][0][id] = 109293
food[food_tag_attributes][0][tag_id] = 2114
food[food_tag_attributes][0][_destroy] = false
# this one is cleared
food[food_tag_attributes][0][id] = 109294
food[food_tag_attributes][1][tag_id] = 2116
food[food_tag_attributes][1][_destroy] = true
This pushes a burden to the client to know the IDs of the food tag records instead of just being able to Set or Clear tags based on the tag id.
Can this be done easily? I'm sure I could write a before_save filter on Food, but it seems like there should be a reasonably generic solution.
There is an option called index: for fields_for in the view helper. You can set the index as your foreign_key. Then instead of sequential or some arbitrary numbers, your foreign_key will be used as the key to refer to your object.
EDIT:
<%= form_for #person do |person_form| %>
<%= person_form.text_field :name %>
<% #person.addresses.each do |address| %>
<%= person_form.fields_for address, **index**: address.id do |address_form|%>
<%= address_form.text_field :city %>
<% end %>
<% end %>
<% end %>

Rails Format Collection_Select Based on two relationships

I'm trying to figure out how to construct a collection_select to include two relationships. Here are my models:
class Country < ActiveRecord::Base
has_many :companies, :dependent => :destroy
end
class Company < ActiveRecord::Base
belongs_to :country
has_many :departments, :dependent => :destroy
end
class Department < ActiveRecord::Base
belongs_to :company
end
When I create a new company I use the following to show a select box based on the relationship.
<%= collection_select(:company, :country_id, Countries.all, :id, :name, :prompt => 'Please select country') %>
But for the departments I'd like to have a select which let's the user select it's company from a select which also includes the companies country, formatted in the following way:
Company 1 - Country 1
Company 2 - Country 1
If i use the following I will only get a list of all the companies which I'd like to be able to see from the list which country they are from.
<%= collection_select(:device, :cabinet_id, Cabinet.all, :id, :name, :prompt => 'Please select cabinet') %>
Is there a way for rails to pull the information for the country into a select and append the entry with it's parent country?
I hope I've worded this question correctly! Sorry if it isn't clear.
Even if #jvnil solution works, I think you should avoid putting this logic in your view.
Instead, you could create an instance method in your Company model and use it in your select.
In your model :
class Company< ActiveRecord::Base
def name_for_select
name + " - " + country.name
end
end
And in your view :
<%= collection_select(:department, :company_id, Company.all, :id, :name_for_select %>
Use
UPDATE: move logic code to model
# company.rb
def company_with_country
"#{name} - #{country.name}" # this is better than using `string + string` since it creates only 1 string
end
# view
collection_select :department, :company_id, Company.includes(:country).all, :id, :company_with_country
UPDATE: faster version because it only uses needed columns
# controller
#companies = Company.joins(:country)
.select('companies.id, companies.name, countries.name AS country_name')
.map { |c| ["#{c.name} - #{c.country_name}", c.id] }`
# view
select :department, :company_id, #companies

rails - grouped_collection_select with 3 tables

The Rails docs example for grouped_collection_select is set up similar to what I need but I want to use 2 selects. The first one will list the continents. Then, based on the continent selection, I want to show a list of cities.
class Continent < ActiveRecord::Base
has_many :countries
# attribs: id, name
end
class Country < ActiveRecord::Base
belongs_to :continent
# attribs: id, name, continent_id
end
class City < ActiveRecord::Base
belongs_to :country
# attribs: id, name, country_id
end
I know the first select would be written as:
<%= f.collection_select :continent_id, Continent.order(:name), :id, :name %>
The second one is giving me problems:
<%= f.grouped_collection_select :city_id, Continent.order(:name), :countries, :name, :id, :name %>
The need for the country table is throwing me off. Any suggestions?

Ruby on Rails collection_select complexity

i have the following Problem, i Have the following in my customer bill view
<%= f.collection_select :product_id,Product.all,:id,:name %>
This is getting list of all the products from "Product" model and giving option to select from it. But i want to select the list of products from the "StoreOpeningStock" model.
I have these in my model
class Product< ActiveRecord::Base
has_many :store_opening_stocks
has_many :customer_bills
attr_accessible :name
end
class StoreOpeningStock < ActiveRecord::Base
attr_accessible :product_id
belongs_to :product
end
class CustomerBill < ActiveRecord::Base
attr_accessible :product_id
belongs_to :product
accepts_nested_attributes_for :store_opening_stock
end
Can anyone guide me how i can get product name and id from store_opening_stock??? Should i use Helpers??? or is there any other way?? Thanks in advance
I tried using helpers
def getting_prod_names
#sto = StoreOpeningStock.all
for x in #sto
[
['{x.product.title}', '{x.product_id}']
]
end
end
getting following output
<%= f.select :product_id, options_for_select(getting_prod_names) %>
ANy Help?? :)
When you create a form the data used to ccreate a collection_select isnt limited to the Class your going to create an object for. You could simply do the following:
<%= f.collection_select :product_id,StoreOpeningStock.all,:product_id ,:name %>
This should to it for you,...
add this to your StoreOpeningStock class:
def name
return self.product.name unless self.product.nil?
""
end
You need to clarify the relationship between your models...
But just to give you an idea. You can define the collection of products you want to display in your controller, inside the action related to the view (where you are displaying the collection).
Controller:
#products= #here you should call all products you want
Then, your collection of products can be displayed like:
<%= f.collection_select :product_id, #products,:id,:name %>
EDIT
You need to revise the relationship between your models. A product has many customer_bills, but are you sure that each customer_bill belongs to a single product?
I think you have a many-to-many relationship, as a customer_bill can also have many products.
If I understand it right, the solution is to create a ProductLine model between this many-to-many relationship.
Also, what is the difference between Product and StoreOpeningStock? What attributes have you included in the StoreOpeningStock?
If you have created this model only to show the availability of products, why don't you add an attribute in the Product model, for example a boolean column called availability.
So you want to find all products that have a StoreOpeningStock.
This is solely a model concern and have nothing to do with helpers.
class Product
# Find all products that have a StoreOpeningStock
def self.in_stock
find(StoreOpeningStock.product_ids)
end
end
class StoreOpeningStock
# Collect all product ids from stocks
def self.product_ids
uniq.pluck(:product_id)
end
end
Now you can use Product.in_stock instead of Product.all to have the only ones in stock.
I'd add a scope to your products model:
class Product< ActiveRecord::Base
has_many :store_opening_stocks
has_many :customer_bills
attr_accessible :name
scope :having_store_opening_stocks, :joins => : store_opening_stocks, :select => 'distinct product.*', :conditions => 'store_opening_stocks.product > 0'
end
Then you can use Product.all.having_store_opening_stocks to select only products with such stocks, for example:
<%= f.select :product_id, Product.having_store_opening_stocks.map { |product| [product.name, product.id] } %>

Resources