simple_fields_for with check_boxes error - ruby-on-rails

Good night friends!
In a form with many through, I need to display all the objects of a given class (tool), with a checkbox field and text field next to it. My form is as follows:
= simple_form_for #service, html: { class: 'form-horizontal' } do |f|
- #tools.each do |tool|
= f.simple_fields_for :instrumentalisations, tool do |i|
= i.input :tool_id, tool.id, as: :check_boxes
= i.input :amount
But I'm getting the following error:
Undefined method `tool_id 'for # <Tool: 0x007faef0327c28>
Did you mean To_gid
Models
class Service < ApplicationRecord
has_many :partitions, class_name: "Partition", foreign_key: "service_id"
has_many :steps, :through => :partitions
has_many :instrumentalisations
has_many :tools, :through => :instrumentalisations
accepts_nested_attributes_for :instrumentalisations
end
class Tool < ApplicationRecord
has_many :instrumentalisations
has_many :services, :through => :instrumentalisations
accepts_nested_attributes_for :services
end
class Instrumentalisation < ApplicationRecord
belongs_to :service
belongs_to :tool
end
Controller
def new
#service = Service.new
#service.instrumentalisations.build
end
def edit
#tools = Tool.all
end
def create
#service = Service.new(service_params)
respond_to do |format|
if #service.save
format.html { redirect_to #service, notice: 'Service was successfully created.' }
format.json { render :show, status: :created, location: #service }
else
format.html { render :new }
format.json { render json: #service.errors, status: :unprocessable_entity }
end
end
end
def service_params
params.require(:service).permit(:name, :description, :price, :runtime, :status, step_ids: [], instrumentalisations_attributes: [ :id, :service_id, :tool_id, :amount ])
end
Thank you!

the error is quite simple: tool doesn't has a tool_id method. BUT, why are you asking this to a tool object instead of a instrumentalisation object?
So, you're trying to create some instrumentalisations but you're passing a tool as object:
f.simple_fields_for :instrumentalisations, **tool** do |i|
fields_for requires a record_name, whith in this case is :instrumentalisations and the 2nd arg is the record_object, which should be an instrumentalisations object and not a tool object.
So to fix it would have to pass an instrumentalisation object. You can accomplish that by:
f.simple_fields_for :instrumentalisations, Instrumentalisation.new(tool: tool) do |i|
Of course this isn't the best solution since if you edit this object would be building a lot of new instrumentalisations.
I'd recommend the cocoon gem, which makes it easier to handle nested forms!

Related

Rails create multiple records from comma delimited form param

I'm having an issue creating multiple option_values from a form param field called "name" that has a value that looks like this: a1,a2,b2,c4. What I would like to do is create an option_value for each one of those entries but I'm not sure how to do it. I know that I need to split the value but I'm just not sure where to do that exactly.
Controller:
class Admin::OptionValuesController < Admin::ApplicationController
before_action :set_option_value, only: [:show, :edit, :update, :destroy]
# GET /option_values
# GET /option_values.json
def index
#option_values = OptionValue.all
end
# GET /option_values/1
# GET /option_values/1.json
def show
end
# GET /option_values/new
def new
#option_value = OptionValue.new
end
# GET /option_values/1/edit
def edit
end
# POST /option_values
# POST /option_values.json
def create
#option_value = OptionValue.new(option_value_params)
respond_to do |format|
if #option_value.save
format.html { redirect_to #option_value, notice: 'Option value was successfully created.' }
format.json { render :show, status: :created, location: #option_value }
else
format.html { render :new }
format.json { render json: #option_value.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /option_values/1
# PATCH/PUT /option_values/1.json
def update
respond_to do |format|
if #option_value.update(option_value_params)
format.html { redirect_to #option_value, notice: 'Option value was successfully updated.' }
format.json { render :show, status: :ok, location: #option_value }
else
format.html { render :edit }
format.json { render json: #option_value.errors, status: :unprocessable_entity }
end
end
end
# DELETE /option_values/1
# DELETE /option_values/1.json
def destroy
#option_value.destroy
respond_to do |format|
format.html { redirect_to option_values_url, notice: 'Option value was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_option_value
#option_value = OptionValue.find(params[:id])
end
# Only allow a list of trusted parameters through.
def option_value_params
params.require(:option_value).permit(:option_type_id, :name)
end
end
Form. Within the main product form this allows me to create an option_type and also at that time create multiple option_values which belong to the option_type: This is what I am doing.
<div class="grid md:grid-cols-1 row-gap-6 col-gap-4 lg:grid-cols-3 mb-4">
<%= form.fields_for :option_types, OptionType.new do |options| %>
<div>
<%= options.label "Option Type Name", class: "text-gray-700" %>
<%= options.text_field :name, class: 'w-full mt-2 px-4 py-2 block rounded bg-gray-200 text-gray-800 border border-gray-300 focus:outline-none focus:bg-white' %>
</div>
<div data-controller="nested-form">
<%= options.fields_for :option_values, OptionValue.new do |ov| %>
<%= render "admin/option_types/option_values_fields", form: ov %>
<% end %>
</div>
<% end %>
</div>
Models:
product:
class Product < ApplicationRecord
has_many_attached :images, :dependent => :delete_all
has_one_attached :main_image, :dependent => :delete_all
has_many :product_option_types, dependent: :destroy, inverse_of: :product
has_many :option_types
has_many :option_values
accepts_nested_attributes_for :option_types
accepts_nested_attributes_for :option_values
has_many :variants, inverse_of: :product, dependent: :destroy
end
Option_type
class OptionType < ApplicationRecord
belongs_to :product
has_many :option_values, dependent: :destroy
accepts_nested_attributes_for :option_values, reject_if: :all_blank, allow_destroy: true
has_many :product_option_types, dependent: :destroy
end
option_value
class OptionValue < ApplicationRecord
belongs_to :option_type
has_many :option_value_variants, dependent: :destroy
has_many :variants, through: :option_value_variants
validates_presence_of :name
validates_uniqueness_of :name, scope: :option_type_id, case_sensitive: false
def create_from_csv(comma_separated_string)
comma_separated_string.split(',').map do |val|
create(name: val)
end
end
end
You can create a method in your OptionType called create_with_values
In it you'd do something like
def self.create_with_values(type, names)
type_instance = create(name: type)
names.split(',').map do |val|
type_instance.option_values.create(name: val)
end
end
Instead of your OptionValuesController, your OptionTypesController's create method should be
def create
OptionType.create_with_values(type: params[:type], values: params[:name])
redirect_to appropriate_path
end
A more railsy way to do this would be with nested attributes but you will have to setup your create_with_values method to transform the incoming comma separated strings into something that the nested attributes can accept.
Look at https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html for more information

3rd level nested model not receiving grandparent ID in form

I have a 3 models. User, CV, and Language. A User has one CV. A CV has many Languages. The User has many Languages through its CV. When I try to save the form I get an error that the Language does not have a User ID. How can I get the User ID to pass through the CV and to the Language in my form?
The CV is receiving the User ID properly. Languages is not.
I am using the Simple-Form and Cocoon gems.
Simplified version of form
= simple_form_for(#cv, url: user_cvs_path) do |f|
= f.simple_fields_for :languages do |language|
From User Model
has_one :cv, dependent: :destroy
has_many :languages, through: :cv, inverse_of: :user
From Cv Model
belongs_to :user
has_many :languages, dependent: :destroy
accepts_nested_attributes_for :languages, allow_destroy: true
From Language Model
belongs_to :user
belongs_to :cv
From the CV Controller
before_action :set_user
def new
#cv = #user.build_cv
#cv.languages.build
end
def create
#cv = #user.create_cv(cv_params)
respond_to do |format|
if #cv.save
format.html { redirect_to user_cv_url(#user, #cv), notice: 'Cv was successfully created.' }
format.json { render :show, status: :created, location: #cv }
else
format.html { render :new }
format.json { render json: #cv.errors, status: :unprocessable_entity }
end
end
end
def cv_params
params.require(:cv).permit(
:user_id,
:first_name,
:middle_name,
:last_name,
... # lots of params left out for brevity
languages_attributes: [
:id,
:cv_id,
:user_id,
:name,
:read,
:write,
:speak,
:listen,
:_destroy])
end
def set_user
#user = current_user if user_signed_in?
end
Your Language model does not need the belongs_to :user. Language belongs to CV and CV belongs to User, so the relation between Language and User is already in place. If you need to access the user for a specific language you can write #language.cv.user
To solve your problem just remove the belongs_to :user from the Language model, remove the user_id from languages_attributes, and remove the user_id from languages table.

rails : association tabel (has_and_belongs_to_many) not save any record

i use rails 5 , simple form. in my app there is a Category model and there is a OnlineProduct model. i dont know why when i want to add some categories to my OnlineProduct association table remain empty and don't change.
Category model:
class Category < ApplicationRecord
has_ancestry
has_and_belongs_to_many :internet_products
end
InternetProduct model:
class InternetProduct < ApplicationRecord
belongs_to :user
belongs_to :business
has_and_belongs_to_many :categories
end
InternetProduct controller:
def new
#internet_product = InternetProduct.new
end
def create
#internet_product = InternetProduct.new(internet_product_params)
respond_to do |format|
if #internet_product.save
format.html { redirect_to #internet_product, notice: 'Internet product was successfully created.' }
format.json { render :show, status: :created, location: #internet_product }
else
format.html { render :new }
format.json { render json: #internet_product.errors, status: :unprocessable_entity }
end
end
end
private:
def internet_product_params
params.require(:internet_product).permit(:name, :description, :mainpic, :terms_of_use,
:real_price, :price_discount, :percent_discount,
:start_date, :expire_date, :couponـlimitation, :slung,
:title, :meta_data, :meta_keyword, :enability, :status,
:like, :free_delivery, :garanty, :waranty, :money_back,
:user_id, :business_id,
categoriesـattributes: [:id, :title])
end
and in the view only the part of who relate to categories :
<%= f.association :categories %>
all the categories list in view (form) but when i select some of them not save in database. in rails console i do this
p = InternetProduct.find(5)
p.categories = Category.find(1,2,3)
this save to database without any problem, what should i do ?
tanks for reading this
I found solution to solve this. when we use has_and_belong_to_many or any other relation , if you want to use collection select in simple_form , in the model also should be add this command for nesting form
accepts_nested_attributes_for :categories
also in the controller in related method for example in the new we should
def new
#internet_product = InternetProduct.new
#internet_product.categories.build
end

Ruby on Rails : Updating multiple models in a single form

I have 3 models User, House and Order.
Order Model
class Order < ActiveRecord::Base
belongs_to :from_house, :class_name => "House"
belongs_to :to_house, :class_name => "House"
belongs_to :user
accepts_nested_attributes_for :from_house, :to_house, :user
end
My House Model.
class House < ActiveRecord::Base
belongs_to :user
belongs_to :place
belongs_to :city
end
My user model.
class User < ActiveRecord::Base
has_many :orders
has_many :houses
end
In my order form I have something like this
<%= form_for #order do |f| %>
... # order fields
<%= f.fields_for :user do |i| %>
... # your from user forms
<% end %>
<%= f.fields_for :from_house do |i| %>
... # your from house forms
<% end %>
<%= f.fields_for :to_house do |i| %>
... # your to house forms
<% end %>
...
<% end %>
I haven't changed much in controller from the default. The controller code
def create
#order = Order.new(order_params)
respond_to do |format|
if #order.save
format.html { redirect_to #order, notice: 'Order was successfully created.' }
format.json { render action: 'show', status: :created, location: #order }
else
format.html { render action: 'new' }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
def order_params
params.require(:order).permit( :shift_date, user_attributes: [:name, :email, :ph_no], from_house_attributes: [:place_id, :floor, :elevator, :size], to_house_attributes: [:place_id, :floor, :elevator])
end
When I submit the form, as expected a Order gets created with a new from_house and to_house along with a new user. But however my user_id in house table remains NULL. How can I make the houses(both from and to) reference the user created after submit.
The User is not logged in, So there is no current_user. We have to create a new user based on the details given. That user has to be associated with the houses (from and to).
I hope I'm clear. If not please let me know.
P.S: This question is an extension to this Ruby on rails: Adding 2 references of a single model to another model
I think this change in app/models/order.rb should do the trick:
class Order < ActiveRecord::Base
belongs_to :user
belongs_to :from_house, class_name: 'House'
belongs_to :to_house, class_name: 'House'
accepts_nested_attributes_for :user, :from_house, :to_house
validates :user, :from_house, :to_house, presence: true
def from_house_attributes=(attributes)
fh = build_from_house(attributes)
fh.user = self.user
end
def to_house_attributes=(attributes)
th = build_to_house(attributes)
th.user = self.user
end
end
Now, try this in your Rails console:
params = { user_attributes: { name: 'New name', email: 'name#example.com' }, from_house_attributes: { name: 'From house name' }, to_house_attributes: { name: 'to house name' } }
o = Order.new(params)
o.save
o.from_house
o.to_house
Cheers!

Has And Belongs to Many Through Save and Update

Here are my models:
class Examination < ActiveRecord::Base
has_many :categorizations
has_many :exam_statuses, :through => :categorizations
end
class Categorization < ActiveRecord::Base
belongs_to :examination
belongs_to :exam_status
end
class ExamStatus < ActiveRecord::Base
has_many :categorizations
has_many :examinations, :through => :categorizations
end
I can assign relations from the console without any problem by typing;
e = Examination.first
e.exam_status_ids = [1,2]
And also in the examinations/index.html.erb file I can list exam_statuses without any problem.
The problem is, I can't update or create any exam_status relations from examinations/_form.html.erb file!
I'm trying to make this with simple_form:
<%= f.association :exam_statuses, as: :check_boxes, label: 'Sınavın Durumu' %>
Its listing all the statuses with checkboxes but not updating them.
Logs saying:
"Unpermitted parameters: exam_status_ids"
And finally my controller, which is generated by "scaffold" by default, for update is:
def update
respond_to do |format|
if #examination.update(examination_params)
format.html { redirect_to #examination, notice: 'Examination was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #examination.errors, status: :unprocessable_entity }
end
end
end
From what your logs say, you should permit the parameter, in the controller:
def examination_params
params.require(:examination).permit(:exam_status_ids)
end
Don't forget to add other parameters in the permit call!
Then you can use it in your controller's action:
def update
...
#examination.update_attributes! examination_params
...
end
I think you need to use accepts_nested_attributes in this case to get it updated.
For more details you can refer this article

Resources