I am currently working on the backend of shop. The Client wants to be able to see a list of all products and update the stock values for all the products in one submission of a form. I have a working solution, but it's a very 'hacky' and introduces a lot of issues. I am new to Ruby on Rails and web development in general so I'm still learning a few of the fundamental conventions and what not.
I will paste my working solution and then attempt to explain the problem I have:
class Product < ActiveRecord::Base
has_many :stocks
...
end
class Stock < ActiveRecord::Base
belongs_to :product
...
end
stock_controller.rb
class StocksController < ApplicationController
def index
#products = Product.all.includes(:stocks)
end
...
def update_current
#stock_params = params[:stock]
#stock_params.each do |stock_params|
params.permit(:current_stock, :product_id)
#stock = Stock.new(stock_params)
#stock.save
end
redirect_to stocks_path, notice: 'Stocks were successfully updated'
end
...
stocks.index.html.erb
...
<%= form_tag url_for(:action => 'update_current') do |f| %>
<% #products.each do |product| %>
<tr>
<td><%= product.product_name %></td>
<td><%= product.minimum_stock %></td>
<td><%= text_field_tag "stock[][current_stock]", product.stocks.last.current_stock %></td>
<%= hidden_field_tag "stock[][product_id]", product.stocks.last.product_id %>
</tr>
<% end %>
<%= submit_tag 'save' %>
<% end %>
...
When I hit the submit button params set is as it needs to be:
console :
Started POST "/stocks/update_current" for 127.0.0.1 at 2013-10-24 11:54:03 +0100
Processing by StocksController#update_current as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"NlabBuwI06t+YN5O6p7dm+Zg2Bwc9uXrKUdWaBqNs9w=", "stock"=>[{"current_stock"=>"1", "product_id"=>"1"}, {"current_stock"=>"2", "product_id"=>"2"}, {"current_stock"=>"3", "product_id"=>"24"}, {"current_stock"=>"4", "product_id"=>"25"}, {"current_stock"=>"5", "product_id"=>"23"}, {"current_stock"=>"6", "product_id"=>"21"}, {"current_stock"=>"7", "product_id"=>"19"}, {"current_stock"=>"8", "product_id"=>"22"}, {"current_stock"=>"9", "product_id"=>"5"}], "commit"=>"save"}
Unpermitted parameters: utf8, authenticity_token, stock, commit
(0.2ms) BEGIN
SQL (136.6ms) INSERT INTO "stocks" ("created_at", "current_stock", "product_id", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Thu, 24 Oct 2013 10:54:03 UTC +00:00], ["current_stock", 1], ["product_id", 1], ["updated_at", Thu, 24 Oct 2013 10:54:03 UTC +00:00]]
(24.2ms) COMMIT
Unpermitted parameters: utf8, authenticity_token, stock, commit
(0.2ms) BEGIN
SQL (0.7ms) INSERT INTO "stocks" ("created_at", "current_stock", "product_id", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Thu, 24 Oct 2013 10:54:03 UTC +00:00], ["current_stock", 2], ["product_id", 2], ["updated_at", Thu, 24 Oct 2013 10:54:03 UTC +00:00]]
(0.7ms) COMMIT
Unpermitted parameters: utf8, authenticity_token, stock, commit
(0.1ms) BEGIN
SQL (0.4ms) INSERT INTO "stocks" ("created_at", "current_stock", "product_id", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Thu, 24 Oct 2013 10:54:03 UTC +00:00], ["current_stock", 3], ["product_id", 24], ["updated_at", Thu, 24 Oct 2013 10:54:03 UTC +00:00]]
(0.6ms) COMMIT
As you can see form the log the authenticity_token, and other params are unpermitted. Now I understand the purpose of the token and the other params, what I do not know, why exactly I am running into this issue.
My guess is the way I am permitting the params. I don't get how to tell strong_params to permit an array of hashes: stock => [{:current_stock, :product_id},{:current_stock, :product_id}, ..., ....]. params.permit(stock: [:current_stock, :product_id]) ???
It doesn't make sense in this case to nest stocks under product, as I am working with a collection of products opposed to a single product.
In an ideal world, I would like to be able to insert the new stock values for all products in one submit and save to the database with one query. I feel as if Ajax may be a viable solution, but again, until I fully understand whats going on I don't want to confuse things even more.
Any solutions or advice is much appreciated. I hope the above makes sense! It's very difficult to articulate these things sometimes.
This may or may not be your problem, but in your update_current method, shouldn't it be stock_params.permit(:current_stock, :product_id) ? Also a minor point, why do you have |f| in your form_tag if you don't use it.
Related
I have 2 models, Lanzadera and Addict.
Lanzadera has_many :addicts
Addict belongs_to :lanzadera
I have a list of Lanzaderas with a "Sign up" button next to them, that will render the Addict new form.
The user will then fill out a form with the Addict attributes, and that addict should be listed in that particular lanzadera. So, it's all about listing addicts in lanzaderas.
Everything looks great, I can see lanzadera_id in the logs, but when I say #lanzadera.addicts.count it will put 0. That means, addicts are being created but are not being assigned to it's lanzadera.
lanzadera_id is being permitted in addict_params in the controller strong parameters.
My question is, how can I pass lanzadera_id in my form when creating a new Addict, so that the addict gets listed within that Lanzadera?
Started POST "/lanzaderas/1/addicts" for 127.0.0.1 at 2014-07-10 19:25:41 +0200
Processing by AddictsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"mJ7pl2xsjhwhdGM2RZBDA+fmD75tpZcsPIaeSwYaBhE=", "addict"=>{"name"=>"Kike", "email"=>"kikeisasi#gmail.com", "city"=>"65", "postal"=>"56478", "street"=>"Aiboa 19, 4", "lanzadera_id"=>""}, "commit"=>"Crear Addict", "lanzadera_id"=>"1"}
(0.2ms) BEGIN
SQL (0.7ms) INSERT INTO "addicts" ("city", "created_at", "email", "name", "postal", "street", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["city", "65"], ["created_at", Thu, 10 Jul 2014 17:25:41 UTC +00:00], ["email", "kikeisasi#gmail.com"], ["name", "Kike"], ["postal", 56478], ["street", "Aiboa 19, 4"], ["updated_at", Thu, 10 Jul 2014 17:25:41 UTC +00:00]]
My guess is that you are missing the landazera_id in the Addicts table in your database. When it begins the INSERT INTO line it doesn't list the id as one of the columns. If you haven't, make sure 'landazera_id' migrated into the table. Rails doesn't do this automatically based on your has_many, belongs_to associations (or at least I've never figured out how to make it automatic) so you'll have to make the migration yourself.
rails g migration add_landazera_id_to_addicts landazera_id:integer
Check the migration in your text editor to make sure it generated correctly. It should include:
def change
add_column :addicts, :landazera_id, :integer
end
I have a multiselect (using bootstrap-multiselect) in my #minisets new form that aims to associate #scales with the #miniset via the #sizes table.
The associations work fine. What I'm stuck on is how to loop through the multiple :scale_id submissions from the multiselect and create lines in the #sizes table for them all.
Following this answer I have been trying to use split and then loop the create but I think the fact that that answer pertains to a HABTM relationship and mine is has_many_through means I need a different solution?
In my minisets controller I have
def new
#miniset = Miniset.new
#miniset.sizes.build
end
def create
#miniset = Miniset.new(miniset_params)
if #miniset.save
params[:scale_id].split(',').each do |id|
#miniset.sizes.create(params[:sizes_attributes])
end
redirect_to #miniset
else
render 'new'
end
end
private
def miniset_params
params.require(:miniset).permit(:name, :release_date, :material, :pcode, :notes, :quantity, :random, productions_attributes: [:id, :manufacturer_id, :miniset_id], sizes_attributes: [:id, :scale_id, :miniset_id], sculptings_attributes: [:id, :sculptor_id, :miniset_id])
end
end
In my view I have
<%= f.fields_for :sizes do |size_fields| %>
<%= size_fields.label :scale_id, simple_pluralize(#miniset.scales.count, 'Scale') %>
<%= size_fields.select :scale_id,
options_from_collection_for_select(Scale.all, :id, :name, #miniset.scales.map(&:id)),
{},
{class: 'multiselect', multiple: true} %>
<% end %>
<script type="text/javascript">
$(document).ready(function() {
$('.multiselect').multiselect();
});
</script>
I'm currently getting error undefined methodsplit' for nil:NilClass` when I submit.
I think that may be because the log shows an empty scale_id passed before the two filled ones and split won't accept nil? Here is the log when submitting TWO scales.
Started POST "/minisets" for 127.0.0.1 at 2014-01-30 10:49:59 +0000
Processing by MinisetsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"R0RxDMwB5/ytSb5qgjIlVR5as0/DTkstgFMDXcefDnc=", "miniset"=>{"name"=>"Test for size", "quantity"=>"10", "random"=>"0", "material"=>"Hard Plastic", "sizes_attributes"=>{"0"=>{"scale_id"=>["", "1", "5"]}}, "pcode"=>"", "release_date(1i)"=>"", "release_date(2i)"=>"", "release_date(3i)"=>"", "notes"=>""}, "Set Scale"=>{"#<ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_CollectionProxy_Size:0x007fcf643c29f0>"=>""}, "commit"=>"Add set"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'd59f28d384d62b71719dd845b4e5353cdd993016' LIMIT 1
Unpermitted parameters: scale_id
SQL (0.9ms) INSERT INTO "minisets" ("created_at", "material", "name", "notes", "pcode", "quantity", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?) [["created_at", Thu, 30 Jan 2014 10:49:59 UTC +00:00], ["material", "Hard Plastic"], ["name", "Test For Size"], ["notes", ""], ["pcode", ""], ["quantity", 10], ["updated_at", Thu, 30 Jan 2014 10:49:59 UTC +00:00]]
SQL (0.6ms) INSERT INTO "sizes" ("created_at", "miniset_id", "updated_at") VALUES (?, ?, ?) [["created_at", Thu, 30 Jan 2014 10:49:59 UTC +00:00], ["miniset_id", 41], ["updated_at", Thu, 30 Jan 2014 10:49:59 UTC +00:00]]
(4.2ms) commit transaction
Completed 500 Internal Server Error in 153ms
NoMethodError (undefined method `split' for nil:NilClass):
app/controllers/minisets_controller.rb:19:in `create'
I'm sure what I have after the split is incorrect but I can't play with it until the split works. I can get rid of the error by adding to_s before the split but I get no better results.
Been making very slow progress on this multiselect for days now so any help very much appreciated.
Thanks to this fantastic youtube video I solved my problem.
My form:
<%= f.fields_for(#size) do |sf| %>
<%= sf.label simple_pluralize(#miniset.scales.count, 'Scale') %>
<%= collection_select( :scales, :id, #all_scales, :id, :name,
{},
{class: 'multiselect', multiple: true}) %>
<% end %>
In my minisets_controller I have the following new and create actions:
def new
#miniset = Miniset.new
#all_scales = Scale.all
#size = #miniset.sizes.build
end
def create
#miniset = Miniset.new(miniset_params)
params[:scales][:id].each do |scale|
if !scale.empty?
#miniset.sizes.build(:scale_id => scale)
end
end
if #miniset.save
redirect_to #miniset
else
render 'new'
end
end
It works perfectly. If anyone else is having the same problem, trying to get multiselects to work in rails with has_many_through, I recommend watching that video. So pleased.
I have a create action in my ProductsController (the params come from a form in my view):
def create
vendor = #current_vendor
product = Product.create(:name => params[:product][:name])
vendor.products << product
vendor.belongings.create(:product_id => product.id, :count => params[:belonging][:count], :detail => params[:belonging][:detail])
if vendor.save
flash[:notice] = "Produkt hinzugefügt!"
redirect_back_or_default root_url
else
render :action => :new
end
end
It creates a variable "vendor", which stores the currently logged-in vendor (Authlogic)
A new Product is created (the product name is from the input field in the form) and stored in the variable "product"
The "product" is being connected to the current vendor
In the belongings table, additional informations to the product are being stored
it saves the whole thing
It's a many-to-many relationship throught the belongings table.
My problem is, the create action always creates the product twice!
Thanks for your help! :)
My console log when I create a new object through my form is:
Started POST "/products" for 127.0.0.1 at 2013-09-15 20:40:26 +0200
Processing by ProductsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"lNk/qQMP0xhlCuGgHtU+d5NEvIlCFcPSKB0FxDZH0zY=", "product"=>{"name"=>"Erdbeeren"}, "belonging"=>{"count"=>"20", "detail"=>"Rot"}, "commit"=>"Create"}
DEPRECATION WARNING: ActiveRecord::Base#with_scope and #with_exclusive_scope are deprecated. Please use ActiveRecord::Relation#scoping instead. (You can use #merge to merge multiple scopes together.). (called from current_vendor_session at /Users/reto_gian/Desktop/dici/app/controllers/application_controller.rb:11)
Vendor Load (0.3ms) SELECT "vendors".* FROM "vendors" WHERE "vendors"."persistence_token" = '04f75db0e2ef108ddb0ae1be1da167536d47b4d79c60ecb443ad2ea5717ecd752388e581f9379746568c72372be4f08585aa5581915b1be64dc412cded73a705' LIMIT 1
(0.1ms) begin transaction
SQL (0.8ms) INSERT INTO "products" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", Sun, 15 Sep 2013 18:40:26 UTC +00:00], ["name", "Erdbeeren"], ["updated_at", Sun, 15 Sep 2013 18:40:26 UTC +00:00]]
(0.8ms) commit transaction
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "belongings" ("created_at", "product_id", "updated_at", "vendor_id") VALUES (?, ?, ?, ?) [["created_at", Sun, 15 Sep 2013 18:40:26 UTC +00:00], ["product_id", 7], ["updated_at", Sun, 15 Sep 2013 18:40:26 UTC +00:00], ["vendor_id", 1]]
(0.9ms) commit transaction
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "belongings" ("count", "created_at", "detail", "product_id", "updated_at", "vendor_id") VALUES (?, ?, ?, ?, ?, ?) [["count", "20"], ["created_at", Sun, 15 Sep 2013 18:40:26 UTC +00:00], ["detail", "Rot"], ["product_id", 7], ["updated_at", Sun, 15 Sep 2013 18:40:26 UTC +00:00], ["vendor_id", 1]]
(0.9ms) commit transaction
Redirected to http://localhost:3000/
Completed 302 Found in 30ms (ActiveRecord: 5.1ms)
I think the problem could be in the line
vendor.products << product
this is adding the variable product (which is Product.create(:name => params[:product][:name]) to vendor.products a second time - which is unnecessary and likely the source of your problem
In my Users table I have boolean columns: owner, manager.
The below does not update the table once the form is submitted.
<%= f.check_box :owner %>
<%= f.label :owner %>
Logs:
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"k0m814s9fRjCUZxeBXn5GO3o5Fq0evZG1Xc7IfUCOYU=", "user"=>{"name"=>"Bob Dylan", "email"=>"bob.dylan#gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "manager"=>"0", "owner"=>"1"}, "commit"=>"Create my account"}
Unpermitted parameters: manager, owner
(0.2ms) begin transaction
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER('bob.dylan#gmail.com') LIMIT 1
Binary data inserted for `string` type on column `password_digest`
SQL (56.3ms) INSERT INTO "users" ("created_at", "email", "name", "password_digest", "remember_token", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["created_at", Thu, 06 Jun 2013 14:57:53 UTC +00:00], ["email", "bob.dylan#gmail.com"], ["name", "Bob Dylan"], ["password_digest", "$2a$10$t5hux4e.jDWS9GH7fJj7Z.gSkLehpJxzfwXOTqbnL6LA7zWZT/11S"], ["remember_token", "J-OFaVfx3a4KMGQ0Q9vttg"], ["updated_at", Thu, 06 Jun 2013 14:57:53 UTC +00:00]]
In your User controller you must have something like:
def user_params
params.require(:user).permit(:manager, :owner)
end
<label>Owner: </label><input type="checkbox" name="owner" value="1" />
The owner key will be present in the post array if the checkbox is checked.
To make the checkbox checked when viewing the form after the form is submitted, you need to add checked="checked" as an attribute on the checkbox element.
You will need to go into your view and create either a checkbox or a radio button for that particular field. In your view, you would do:
form_for #user do |f|
f.label :owner
f.check_box :owner
f.submit
end
You would wrap this in the html code as needed.
I have model Document:
class Document < ActiveRecord::Base
belongs_to :company
validates :name, :presence => true
end
And two classes inherited from Document:
License:
class License < Document
mount_uploader :file, DocumentUploader
end
And Certificate
class Certificate < Document
mount_uploader :file, DocumentUploader
end
And when I try to do current_company.licenses.create(...) or same action for the certificate, always all params are saved, besides file, which always is nil
Also I've tried to mount file inside of Document model... Help me please.
Here is logs:
Started POST "/companies/1/verified" for 127.0.0.1 at Mon Mar 19 09:33:41 +0200 2012
Processing by CompaniesController#verified as HTML
Parameters: {"verified"=>{"certificate"=>{"name"=>"Certificate", "file"=>"test.png"}, "insured"=>"2000000", "suppliers"=>"", "license"=>{"name"=>"License", "file"=>"test.png"}}, "authenticity_token"=>"0hIn41Tjonm/AXZBKM1PE/tjQxJDLqZaojMTHDoZq2k=", "id"=>"1", "utf8"=>"✓", "commit"=>"Update verifications"}
Company Load (0.7ms) SELECT "companies".* FROM "companies" WHERE "companies"."id" = 1 LIMIT 1
(0.1ms) BEGIN
SQL (0.8ms) INSERT INTO "documents" ("company_id", "created_at", "file", "name", "type", "updated_at", "verified") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["company_id", 1], ["created_at", Mon, 19 Mar 2012 07:33:41 UTC +00:00], ["file", nil], ["name", "License"], ["type", "License"], ["updated_at", Mon, 19 Mar 2012 07:33:41 UTC +00:00], ["verified", false]]
(0.6ms) COMMIT
(0.1ms) BEGIN
SQL (0.5ms) INSERT INTO "documents" ("company_id", "created_at", "file", "name", "type", "updated_at", "verified") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["company_id", 1], ["created_at", Mon, 19 Mar 2012 07:33:41 UTC +00:00], ["file", nil], ["name", "Certificate"], ["type", "Certificate"], ["updated_at", Mon, 19 Mar 2012 07:33:41 UTC +00:00], ["verified", false]]
(0.4ms) COMMIT
Redirected to http://localhost:3000/company/profile
Completed 302 Found in 18ms (ActiveRecord: 3.3ms)
Thanks.
Are you sure you've set the form that you've uploading the file with to have a multipart payload?
If not the file won't get sent on submission, and rails will only receive the textual form data. Inside the form_helper tag you'll need to add.
:html => {:multipart => true}