I'm new to Rails and built something based on this
http://railscasts.com/episodes/403-dynamic-forms
but I have a problem with storing data in the additional fields...
I have a ProductType object that has many ProductField objects. The ProductField object also belongs to a ProductType and Product object belongs to a ProductType.
So,new dynamic fields can easily be added via the constructor ProductType, but when I try to set data in this fields via Product controller nothing happens.
I am sure that problem is related to use strong parameters, but fix described here and here did't help.
product.rb
class Product < ActiveRecord::Base
belongs_to :product_type
serialize :properties, Hash
end
product_type.rb
class ProductType < ActiveRecord::Base
has_many :fields, class_name: "ProductField"
accepts_nested_attributes_for :fields, allow_destroy: true
end
product_field.rb
class ProductField < ActiveRecord::Base
belongs_to :product_type
end
products_controller.rb
class ProductsController < ApplicationController
def new
#product = Product.new(product_type_id: params[:product_type_id])
end
def product_params
params.require(:product).permit(:name, :price, :product_type_id, {:properties => []})
end
product_type_controller.rb
class ProductTypesController < ApplicationController
def product_type_params
params.require(:product_type).permit(:name, fields_attributes: [:id, :name, :field_type, :required, :product_type_id])
end
In console log:
Unpermitted parameters: properties
Started PATCH "/products/4" for 127.0.0.1 at 2013-10-04 22:54:59 +0400
Processing by ProductsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"my3ra60OUXexmmguk2eqRetizx3tWPMq04Z2PnODJMQ=", "product"=>{"product_type_id"=>"1", "name"=>"Product1", "properties"=>{"gjfghjf"=>"123", "123"=>[""]}, "price"=>"10"}, "commit"=>"Update Product", "id"=>"4"}
Product Load (0.3ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT 1 [["id", "4"]]
Unpermitted parameters: properties
P.S: maybe someone faced a similar problem when watching a podcast?
If you want to return a nested hash as a parameter you have to name the keys in the array in permit.
class ProductsController < ApplicationController
def new
#product = Product.new(product_type_id: params[:product_type_id])
end
def product_params
params.require(:product).permit(:name, :price, :product_type_id, {:properties => [:foo, :bar, :id]})
end
If you are generating the keys dynamically and can't code them into the permit statement then you need to use this style:
def product_params
params.require(:product).permit(:name, :price, :product_type_id).tap do |whitelisted|
whitelisted[:properties] = params[:product][:properties]
end
end
It's not the most friendly code for a new user, I just finished the 3 course rails certificate at UW and they never even covered .tap.
This is not my work, I'm still just understanding the deeper parts of .permit like this. This is the blog entry I used: Strong Parameters by Example
Related
I want to create an Invoice and the regarding InvoiceItems at the same time. While Invoice has_many :invoice_items and an InvoiceItem belongs_to :invoice. How do I perform such action in Rails 7 so that a User can add multiple invoiceItems to their invoice via Turbo? I dont need to know how TurboStreams and stuff work, since I am familiar, but I just cant get the InvoiceItems to be created at the same time as the Invoice.
I already found this post, but could not get any useful information out of it.
Models
Invoice.rb
class Invoice < ApplicationRecord
belongs_to :project
has_many :invoice_items, foreign_key: :invoice_id # not sure if this foreign_key is necessary
accepts_nested_attributes_for :invoice_items
end
invoice_item.rb
class InvoiceItem < ApplicationRecord
belongs_to :invoice
end
Controllers
Invoice_controller.rb
def create
#project = Project.find(params[:project_id])
#client = Client.find(params[:client_id])
#invoice = #project.invoices.new(invoice_params)
#invoice_item = #invoice.invoice_items.new
#invoice.invoice_items_attributes = [:invoice_id, :amount]
#invoice.client_id = #client.id
respond_to do |format|
if #invoice.save
....
def invoice_params
params.require(:invoice).permit(... :invoice_item, invoice_item_attributes: [:id, :invoice_id, :amount, ...])
end
Currently I try using a form_for inside of the Invoice form like:
<%= form.fields_for #invoice.invoice_items.build do |lorem| %>
Which gives me following error in the console (but saves the invoice as expected:
Unpermitted parameter: :invoice_item. Context: { controller: InvoicesController, action: create, request: #<ActionDispatch::Request:0x000000010a0c8d88>, params: {"authenticity_token"=>"[FILTERED]", "invoice"=>{..., "invoice_item"=>{"invoice_id"=>"", "amount"=>"3"}}, "button"=>"", "controller"=>"invoices", "action"=>"create", "user_id"=>"1", "client_id"=>"1", "project_id"=>"1"} }
notice that the invoice_id is not passed to the invoice_item.
Via console something like
#invoice = Invoice.new
#invoice.invoice_items.new(amount: "3", ...)
#invoice.save!
Does work weirdly but it does not translate to my code.
What am I doing wrong here?
# invoice_item_attributes is wrong
def invoice_params
params.require(:invoice).permit(... :invoice_item, invoice_item_attributes: [:id, :invoice_id, :amount, ...])
end
Should be
# invoice_items_attributes is right
def invoice_params
params.require(:invoice).permit(... :invoice_item, invoice_items_attributes: [:id, :invoice_id, :amount, ...])
end
Notice the missing 's'.
https://www.ombulabs.com/blog/learning/rails/nested-forms.html
After following the GoRails screencast on how to properly set nested form attributes in rails, I still came across errors. I eventually could trace them and found this neat post which game the hint to use inverse_of and autosave: true. I am not 100% sure what those do, even though I will read now to find out, but my stuff is working properly now :)
Modified Model
class Invoice < ApplicationRecord
belongs_to :project
has_many :invoice_items, inverse_of: :invoice, autosave: true
accepts_nested_attributes_for :invoice_items
...
I am currently creating a WebApp using RoR 4 and I am using has_many, though: associations between my databases.
I have 3 models Users, UsersSubject and Subjects given below:
class UsersSubject < ActiveRecord::Base
belongs_to :users
belongs_to :subjects
end
class Subject < ActiveRecord::Base
has_many :users_subjects
has_many :users, through: :users_subjects
end
class User < ActiveRecord::Base
has_many :users_subjects, :class_name => 'UsersSubject'
has_many :subjects, through: :users_subjects
accepts_nested_attributes_for :subjects
end
I am trying to populate the UsersSubject database when I am updating the User using the User controller. Here is my from the partial form of the User:
<div class="control-group nested-fields">
<div class="contols">
<%= f.fields_for :subject do |subject| %>
<%= subject.label "Subjects" %></br>
<%= subject.collection_select(:subject_id, Subject.all, :id, :name) %>
<% end %>
</div>
</div>
and here is my controller:
def edit
user_id = current_user.id
subject_id = Subject.where(:name => params[:name])
#user_sub = UsersSubject.new(user_id: user_id, subject_id: subject_id)
#user_sub.save
end
When I do this the controller populate the UsersSubject database with the correct user_id but the subject_id is always nil. The Subject database is already populate using the seed.rb file.
Can someone help me understand why this is happening and help my fix it?
Thanks in advance
EDIT
Here is my development.log
Started GET "/users/edit" for ::1 at 2016-05-31 18:27:33 +0100
Processing by Users::RegistrationsController#edit as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"ZGvBq1uFUi1RxcInKFz1TnUs2ZzlZsP29aW1mzQBOVBTLm/Dq3C42cQQX0ksmBv95/qHnk08bG3f5u1v9taZgw==", "user"=>{"address"=>"", "city"=>"London", "postcode"=>"", "country"=>"United Kingdom", "subject"=>{"subject_id"=>"1"}}, "commit"=>"Update"}
Moved controller code to the update action
def update
user_id = current_user.id
subject_id = params[:user][:subject][:subject_id] unless params[:user].nil?
#user_sub = UsersSubject.new(user_id: user_id, subject_id: subject_id)
#user_sub.save
end
Added the subject permission in the application_controller.rb:
class ApplicationController < ActionController::Base
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:last_name,
:first_name, :email, :password, :current_password,
subject_attributes: [:id, :subject_id]) }
end
end
This permits the update action to access the subject_attributes
Changes in the user model
class User < ActiveRecord::Base
has_many :users_subjects, :class_name => 'UsersSubject', dependent: :destroy
has_many :subjects, through: :users_subjects
accepts_nested_attributes_for :subjects
end
Have you looked into using the cocoon gem?
You might also want to take a look at the params you're passing into subject_id, it looks like the full object that you're grabbing from it's name. Print out the "subject_id" that you currently have.
There is a great railscasts video on this topic you might want to look into, the membership is well worth it.
Check your params in your rails server log. From what I guess it should be coming like this
"user" => {
"name"=>"Michael Jackson",
"subject" => {
"subject_id"=>"1"
}
}
But in your controller method, you're trying to access it like params[:name], instead you should do something like this, based on how you've nested in your form.
subject_id = params[:user][:subject][:subject_id]
subject_id is nested inside subject, which is nested inside the user object.
Hope this helps. :)
Reference code:
Do this to your edit action.
def edit
p params #this print out the inspected params in the console log
fail
#this stops the code from executing further, by raising an error.
#So we can inspect the params passed to our method in server log
...
end
Go to your form in your browser, and after that clear your rails server console, (Command + K) for mac, and For ubuntu, press keep pressing enter to move the current log up the screen. Now click submit, and check the server log. You'll notice something like
Started POST "/users"
Processing by UsersController#edit
Parameters: {"utf8"=>..... "user" => { "name" => "Michael Jackson", "subject" => { "subject_id" => "1" }}}
So what you want to do is, you need to create a new many to many relation between User and Subject. You're getting the user_id from devise's current_user and subject_id in your edit method from the params, as I defined. How you're receiving params might differ from how I mentioned.
Final code
def edit
user_id = current_user.id
subject_id = params[:user][:subject][:subject_id]
#user_sub = UsersSubject.new(user_id: user_id, subject_id: subject_id)
#user_sub.save
end
I have got a Products class,Products are visible to zero or many roles. So, I have created a polymorphic model called content_roles,which stores the id of the role and content_id (which will be product_id,or event_id),and content_type(product,event etc).
I am using nested_form gem for accepting the role id(using check_box) to store the product and role relation in content_role
I get an error no implicit conversion of String into Integer in Products#create function
Parameters: {"utf8"=>"✓", "authenticity_token"=>"xxxxxxxxxxxxxxxxxxxxdLH99ZWLrf8dgT3gcBops=", "product"=>{"product_name"=>"some product", "product_description"=>"some product description", "content_roles_attributes"=>{"role_id"=>["1", "2", ""]}}, "commit"=>"Create Product"}
in my view I have written
= f.simple_fields_for :content_roles_attributes do |role|
= role.input :role_id,label: "visible to", as: :check_boxes,label: "Role",collection: Role.all,:required=>true
the controllers permitted params looks like
def create
#Getting the error at this line
#product = Product.new(product_params)
respond_to do |format|
if #product.save
end
def product_params
params.require(:product).permit(:product_description,:product_name,
content_roles_attributes: [:id,role_id: []],
multimedia_attributes:[:asset,:_destroy,:id])
end
the product model looks like:
class Product
has_many :content_roles, as: :content
has_many :multimedia ,as: :storable
# Nested attributes
accepts_nested_attributes_for :multimedia
accepts_nested_attributes_for :content_roles
end
and this is the content_role model
class ContentRole < ActiveRecord::Base
belongs_to :content, polymorphic: true
belongs_to :role
belongs_to :news
belongs_to :product
end
I have got a Products class,Products are visible to zero or many roles . so i have created a polymorphic model called content_roles,which stores the id of the role and content_id (which will be product_id,or event_id),and content_type(product,event etc).
I am using nested_form gem for accepting the role id(using check_box) to store the product and role relation in content_role
the Issue I am facing is I am not able to create a content_role record . in my logs i get unpermitted parameters : role_id
Parameters: {"utf8"=>"✓", "authenticity_token"=>"xxxxxxxxxxxxxxxxxxxxdLH99ZWLrf8dgT3gcBops=", "product"=>{"product_name"=>"some product", "product_description"=>"some product description", "content_roles_attributes"=>{"role_id"=>["1", "2", ""]}}, "commit"=>"Create Product"}
in my view I have written
= f.simple_fields_for :content_roles_attributes do |role|
= role.input :role_id,label: "visible to", as: :check_boxes,label: "Role",collection: Role.all,:required=>true
the controllers permitted params looks like
def product_params
params.require(:product).permit(:product_description,:product_name,
content_roles_attributes: [:role_id,:id],
multimedia_attributes:[:asset,:_destroy,:id])
end
the product model looks like
class Product
has_many :content_roles, as: :content
has_many :multimedia ,as: :storable
# Nested attributes
accepts_nested_attributes_for :multimedia
accepts_nested_attributes_for :content_roles
end
and this is the content_role model
class ContentRole < ActiveRecord::Base
belongs_to :content, polymorphic: true
belongs_to :role
belongs_to :news
belongs_to :product
end
Try changing your product_params to:
def product_params
params.require(:product).permit(
:product_description,
:product_name,
content_roles_attributes: [:id, role_id: []],
multimedia_attributes: [:asset, :_destroy,:id]
)
end
Order.rb:
class Order < ActiveRecord::Base
has_one :review
end
Review.rb:
class Review < ActiveRecord::Base
belongs_to :order
end
I need to build a review, im using this method:
class OrdersController < ApplicationController
def build_review
#review = Review.new(:order => #order)
end
but i get this error:
Can't mass-assign protected attributes: order
any ideas?
You need to white list order for mass assignment via a params hash. Read http://api.rubyonrails.org/classes/ActiveModel/MassAssignmentSecurity/ClassMethods.html
Add this to the Review model
attr_accessible :order
It would be better to use #review = #order.build_review instead of adding this to attr_accessible. Just in case ;)