rails 3.2 update has_many :through - ruby

I'm pretty close on this one but caught up on a minor detail. I am trying to update a has_many :through relationship. When I submit the edit form I am unable to extract the appropriate attribute that I want to update. The loop that updates the qty shipped is not updated that field with the correct value. How can I extract only the qty_shipped attribute from the params[:product_shipments] hash?
This is the contents of my params[:product_shipments] hash that the update action is working with
"product_shipments"=> {"82"=>{"qty_shipped"=>"234"},
"83"=>{"qty_shipped"=>"324"},
"84"=>{"qty_shipped"=>"324"}},
"commit"=>"Update Shipment", "id"=>"250"}
Which has all the information I need to update the shipment because the #shipment.product_shipments loop limit the update to only the shipment_id applicable. My problem is that this is the following sql called by update action
ProductShipment Load (0.3ms) SELECT `product_shipments`.* FROM `product_shipments` WHERE `product_shipments`.`shipment_id` = 250
BEGIN
UPDATE `product_shipments` SET `qty_shipped` = 1 WHERE `product_shipments`.`id` = 82
COMMIT
BEGIN
UPDATE `product_shipments` SET `qty_shipped` = 1 WHERE `product_shipments`.`id` = 83
COMMIT
BEGIN
UPDATE `product_shipments` SET `qty_shipped` = 1 WHERE `product_shipments`.`id` = 84
COMMIT
And here is the update action that produces the above sql:
def update
#shipment = Shipment.find(params[:id])
#shipment.update_attributes(params[:shipment])
#shipment.product_shipments.each do |shipment|
shipment.update_attributes(:qty_shipped=> params[:product_shipments])
end
respond_with #shipment, :location => shipments_url
end
Using rbates nested_forms gem is not preferred because I want to figure this out for the purposes of learning how rails works.
<%= hidden_field_tag("product_shipments[][#{product_shipment.id}]") %>
<%= hidden_field_tag("product_shipments[][product_id]", product_shipment.id) %>
<%= text_field_tag "product_shipments[][qty_shipped]", product_shipment.qty_shipped,:class => 'shipment_qty_field'%>&nbsp<%=#product.product_name %>

#shipment.product_shipments.each do |product_shipment|
product_shipment.update_attributes(:qty_shipped => params[:product_shipments][product_shipment.id][:qty_shipped])
end
You shouldn't have to do all this, just use Nested Forms. This is Rails!
http://railscasts.com/episodes/196-nested-model-form-part-1
Your params should look like this
{:product_shipments => { 79 => { :qty_shipped => 450 }, 80 => { :qty_shipped => 35 } }, :shipment_id => 1 }
To get that you should name your fields like this
<input name="product_shipments[79][qty_shipped]" value="450" />
<input name="product_shipments[80][qty_shipped]" value="35" />
To generate that,
<% #shipment.product_shipments.each do |product_shipment| %>
<%= text_field_tag "product_shipments[#{product_shipment.id}][qty_shipped]", product_shipment.qty_shipped || 0 %>
<% end %>

Related

Why is it showing identical data at each iteration of .each

I have tried making the data according to #wifis (based on mac_id) is generated but currently the data is identical with the following code -
Controller:
class DashboardsController < ApplicationController
before_action :authenticate_user!
def show
#wifis = Wifi.where(user_id: current_user.id)
pdaily_number_of_logins_data = []
phourly_total_traffic_data = []
pper_user_monthly_traffic_data = []
#wifis.each do |wifi|
daily_number_of_logins_data = Radacct.fetch("SELECT gd_day(acctstarttime, 'UTC') AS date, COUNT(*) AS count FROM radacct WHERE (`calledstationid` = ?) AND (`acctstarttime` >= ?) GROUP BY date", wifi.mac_id, 1.month.ago)
pdaily_number_of_logins_data += daily_number_of_logins_data.to_a if daily_number_of_logins_data.to_a
#daily_number_of_logins = pdaily_number_of_logins_data.collect{|i| [i[:date],i[:count]]}
hourly_total_traffic_data = Radacct.fetch("SELECT gd_hour_of_day(acctstarttime, 'UTC') AS hour_of_day, ROUND(((SUM(acctinputoctets)+SUM(acctoutputoctets))/1049000)) AS totaltraffic FROM radacct WHERE (`calledstationid` = ?) AND (`acctstarttime` >= ?) GROUP BY hour_of_day", wifi.mac_id, 1.day.ago)
phourly_total_traffic_data += hourly_total_traffic_data.to_a if hourly_total_traffic_data.to_a
#hourly_total_traffic = phourly_total_traffic_data.collect{|i| [i[:hour_of_day],i[:totaltraffic]]}
per_user_monthly_traffic_data = Radacct.fetch("SELECT username, ROUND(((SUM(acctinputoctets)+SUM(acctoutputoctets))/1049000)) AS `traffic` FROM `radacct` WHERE (`calledstationid` = ?) AND (`acctstarttime` >= ?) GROUP BY username", wifi.mac_id, 1.month.ago)
pper_user_monthly_traffic_data += per_user_monthly_traffic_data.to_a if per_user_monthly_traffic_data.to_a
#per_user_monthly_traffic = pper_user_monthly_traffic_data.collect{|i| [i[:username],i[:traffic]]}
end
end
end
and View:
% #wifis.each do |wifi| %>
<h1><%= wifi.mac_id %></h1>
<%= line_chart #daily_number_of_logins, { discrete: true, library: {hAxis: {title: "Date"}, vAxis: {title: "Number of Logins (daily)"}}} %>
<%= column_chart #hourly_total_traffic, {library: {hAxis: {title: "Hour"}, vAxis: {title: "Traffic in Megabytes (hourly)"}}} %>
<%= pie_chart #per_user_monthly_traffic, discrete: true %>
<% end %>
i have tried the above based on this answer but the problem that i am having is #wifi object is used in both controller and views: Loop within Loop in Rails Controller
The problem is that loop in your controller action runs and overwrites the instance variables on each iteration. So only the last iteration remains. If you wanted to save data for each wifi, you need another data structure, not plain ivars. Perhaps a hash where wifi is the key.
#counters = {}
#wifis.each do |wifi|
#counters[wifi] ||= {}
#counters[wifi][:daily_number_of_logins] = ...
Then in the view
<% #wifis.each do |wifi| %>
<%= line_chart #counters[wifi][:daily_number_of_logins] %>
<% end %>

ajax-datatables-rails How to add buttons in ajax response

My problem is that I don't know how to add different buttons that remain in a condition using the gem for server side processing ajax-datatables-rails. For example before trying to do sever side processing I had this in my view.
<% if current_user.sales_manager? || current_user.admin? %>
<td>
<%= link_to t('.destroy', :default => t("helpers.links.destroy")),
bill_sale_path(bill_sale),
:method => :delete %>
</td>
<% end %>
and following the tutorial in the main page of the gem, in bill_sale_datatable.rb I have this.
def data
records.map do |record|
[
link_to('Show', bill_sale_path(record), class: 'btn-xs btn-link'),
record.customer.name,
record.seller.name,
l(record.date_sold, format: :short),
record.payment_type.name,
record.payed,
number_to_currency(record.total),
CONDITION AND BUTTON HERE
]
end
end
Then, how do I use the server side processing to provide different buttons that belongs to an if condition?
While I haven't used the gem, I have used datatables with Rails and this is what I did:
Store the button as a variable before the array and then just call the variable in the array.
records.map do |record|
record_button = ''
record_button = button_to('A', button1_path) if condition1
record_button = button_to('B', button2_path) if condition2
record_button = button_to('C', button3_path) if condition3
record_button = button_to('D', button4_path) if condition4
[
link_to('Show', bill_sale_path(record), class: 'btn-xs btn-link'),
record.customer.name,
record.seller.name,
l(record.date_sold, format: :short),
record.payment_type.name,
record.payed,
number_to_currency(record.total),
record_button
]
end
You could also use a case statement if that suited your needs.
Make sure you add
def_delegator :#view, :button_to
To the top of your file as per:
https://github.com/jbox-web/ajax-datatables-rails#using-view-helpers

populate dropdown with two fields in rails 4 ajax

I am having trouble populating a field with two columns concatenated. For example: I want to populate Worker field with both first name and last name in AJAX rails 4.
Currently I'm populating with just first name using
<%= collection_select(:work_order ,:worker_id ,Worker.all, :id ,:first_name) %>
You can do something like:
<%= f.input :user_id, :as => :select, :collection => [[current_user.firstname+' ' +current_user.lastname, current_user.id]], :label=>"Names"%>

accessing mongodb document field names and its values through iteration

Let's say I have a mongodb document in the products collection:
{
"_id" : ObjectId("51b1eac0311b6dd93a000001"),
"name" : "Apple",
"price" : "34.45"
}
products_controller.rb for def show part:
def show
#product = Product.find(params[:id])
end
I imagine the code would seem like below in the show.html.erb:
<% #product.each do |f|%>
<p>f.label</p> # this is only an image code
<p>f.value</p> # this is only an image code
<% end %>
How the code at the lines 2 and 3 on Rails 3 should look like in a generic way so it would show like? :
name: Apple
price: 34.45
Number of fields can be 20, so I don't want to write the same code for 20 fields.
I'm using Rails 3 with Mongoid. I think it's not a mongodb-specific question.
Try this:
foreach (var item in YourcollectionName)
{
var name = item.name;
var price = item.price;
}
Mongoid models have an attributes method that returns a hash of the attributes. If you iterate over that hash you'll be yielded the name and value of each entry.
For example
<% #product.attributes.each do |name, value| %>
<p>
<%= name%> : <%= value %>
</p>
<% end %>
You'd need some more sophisticated formatting code for pretty output for all the kinds of values you might get (dates, arrays, hashes etc.)

Rails 3: Loop shows more records than there should be

Not exactly sure of what to call this issue. Still new to rails.
Situation: An auction contains many lots.
I'm showing an auction's lots in a url like /auctions/3/lots/.
View:
<ul>
<% #lots.each do |lot| %>
<li><%= lot.auction_id %>: <%= lot.id %></li>
<% end %>
</ul>
Outputs this:
<ul>
<li>3: 1</li>
<li>3: </li>
</ul>
I only have one lot in my database. Not sure where the extra loop instance is coming from.
This happens on any lot listing no matter which auction I'm looking at.
Also,
<%= #lots.length %> displays 2
<%= #lots.size %> displays 2
<%= #lots.count %> displays 1
My lots_controller looks like this:
def index
#auction = Auction.find(params[:auction_id])
#lots = #auction.lots
end
def create
#auction = Auction.find(params[:auction_id])
#lot = #auction.lots.build(params[:lot])
if #lot.save
redirect_to auction_lots_path, :notice => 'Lot was successfully created.'
else
render :action => "index"
end
end
My models:
class Auction < ActiveRecord::Base
...
has_many :lots
end
class Lot < ActiveRecord::Base
belongs_to :auction
...
end
The ...s are just attr_accesssible and validates lines.
The log when I hit the page was requested, here it is.
Started GET "/auctions/8/lots" for 127.0.0.1 at 2013-02-13 16:35:51 -0500
Processing by LotsController#index as HTML
Parameters: {"auction_id"=>"8"}
Auction Load (0.1ms) SELECT "auctions".* FROM "auctions" WHERE "auctions"."id" = ? LIMIT 1 [["id", "8"]]
Lot Load (0.2ms) SELECT "lots".* FROM "lots" WHERE "lots"."auction_id" = 8
[#<Lot id: 18, description: "1923 Morgan", lot_number: 1, auction_id: 8, created_at: "2013-02-13 17:20:04", updated_at: "2013-02-13 17:20:04">]
Rendered layouts/_messages.html.erb (0.1ms)
Lot Exists (0.2ms) SELECT 1 AS one FROM "lots" WHERE "lots"."auction_id" = 8 LIMIT 1
Rendered lots/index.html.erb within layouts/application (9.4ms)
Completed 200 OK in 21ms (Views: 17.8ms | ActiveRecord: 0.5ms)
Update:
Someone mentioned that it looks like I'm using #auction.lots.build somewhere.
And yes, I am. I have a form on the same page (index) where I can add lots.
<%= form_for(#auction.lots.build, :url => auction_lots_path(#auction)) do |f| %>
...
<% end %>
Changing #auction.lots.build got rid of the extra row, although now I can't create lots successfully. I'm not sure what to do. I probably have to set up something in the index method of the lots_controller, but I don't know what.
Any help is appreciated.
This would happen in your create method if the lot failed to save. Because you used #auction.lots.build, that appends a lot record to the auction. If it doesn't save properly, it's still there unsaved. That explains why the "mystery" one doesn't have an id, and also why:
<%= #lots.size %> displays 2
<%= #lots.count %> displays 1
#lots.count is a database query, but #lots.size is just the size of the array in memory.
I would probably do something more like this in the create action:
def create
#auction = Auction.find(params[:auction_id])
#lot = #auction.lots.create!(params[:lot])
redirect_to auction_lots_path, :notice => 'Lot was successfully created.'
rescue ActiveRecord::RecordInvalid
render :action => "index"
end
... but of course others will prefer using if/else rather than rescuing the exception. There are other ways around this. You could do #auction.reload.lots to cull the unsaved one, but that's a little wierd. The normal rails thing to do in this case is re-render the form with the validation errors displayed and ask the user to fix them and try creating again.
This should help:
def create
params[:lot].merge!({:auction_id => params[:auction_id]})
#lot = Lot.new(params[:lot])
if #lot.save
redirect_to auction_lots_path, :notice => 'Lot was successfully created.'
else
render :action => "index"
end
end

Resources