rails active record persistance problem - ruby-on-rails

I have an orders object with a status field. I would like to retrieve all orders with status=1 (so I know what is being modified) and then update them to have a status=2. My code is as follows:
#new_orders=Order.where("status=1")
Order.where("status=1").update_all :status=>2
The problem is that #new_orders is not set until the view uses the variable. I am guessing this has to do with lazy loading. How do I get around this so I can display all records that have been modified?

Try adding .all or .to_a to the end of your relation:
#new_orders = Order.where(:status => 1).all

Related

How to set parent attribute based on child attributes

I have a situation where a parent Order has multiple child Items. Both Order and Item have a status_id column. I want the user to update the status_id of the Item, and then when all of the Items have a status_id, then the Order's status_id should be auto-set (to some value based on what Item status_ids are).
The code that I currently have is this:
class Item
after_save :set_order_status_id
def set_order_status_id
if self.order.items.where(status_id:nil).blank?
self.order.update_attributes(status_id:X)
end
end
end
Ths is pretty smelly code because it violates SRP, uses a callback, and is pretty inefficient, considering that this means if an Order has 5 Items and all 5 Items are being updated, after EACH Item update, the set_order_status_id method is called, and a database query is run.
So... is there a better way of writing this to avoid these issues? Particularly I'm interested in removing the inefficiency with constantly checking the parent Order's other child Items' statuses... because again if an all 5 Items is updated at once, it's silly to check after each and every update when it should just wait until the 5th update.... Does Rails have a magical way of doing this?
The answer is no, there is no magic way to do that using the framework. Your best option is to use a customised solution and run your check only after your items update. Something like:
...your code...
order.items.update_all(whatever) <-- update items
update_status(order) <-- update order status
...
def update_status(order)
return if order.items.where(status_id: nil).exist? <-- more efficient
update_attributes(status_id: X)
end
the method could also be in the Offer model for simplicity.
If the order status can be derived from the item status at any point in time, it may be better to avoid setting it in the database entirely. You can instead create an accessor to query it on-demand:
class Order < ActiveRecord::Base
def status
# memoize the calculation, including nils
return #status if defined? #status
item_statuses = items.pluck(:status).uniq
# your logic here:
# 1. check for any nils
# 2. check for any 'pending', etc.
#status = 'pending'
end
end
Whether this alternate solution fits your needs depends on your database read patterns.

proper way to debug sql result in rails

I'm currently using
puts #object.inspect
to debug the results of queries in rails, but the output doesn't seem to include any joins I've defined for the query. How do I get the full array to show?
For example, if I do the following
#object = Object.joins("JOIN associations ON associations.id = object.association_id")
.where(:id => params[:object_id])
.select("objects.*, associations.*")
.first
puts #object.inspect
I get the all the Object fields in my debug array, but none of the association fields. Yet they are there when I try to use them in my view (#object.association_field etc)
PS - the above query looks ugly, I'm only trying to pull one record, but I was getting various errors if I tried to use .find() instead of .where().first. Suggestions welcome on how to make it more railsy
Why not the simplest possible way:
#object = Object.find(params[:object_id])
#association = #object.associations.first
puts "#{#object.inspect}, #{#association.inspect}"
(assuming that has_many :associations is defined in Object)
And the reason you are not getting fields for association is because the only thing that joins does is joining to another table in SQL. It does not fetch joined data. select only select subset of object's attributes, I think the rest is just ignored.

SQL command execution sequence in Ruby

I am displaying a list of items on my view. My controller class executes a sql in order to get the list. Also I am updating some values in table once the list if fetched. The problem is the values are being set before the select statement. Below is the controller code:
#orders = List.select("itemname,tableno,quantity,itmstatus").where(:tableno => "01")
List.where(:tableno => "01").update_all(:ordstatus => 'displayed',:itmstatus => 'displayed')
My view displays different fields retrieved in #orders. Now based on itemstatus value I need to set the text color code in my view. So once my select statement is executed, I set the itmstatus value to some other value. But in my view the #orders has the updated value (which I am doing after select). I checked on server side and the select statement is executed after the update statement which I think might be the case for having updated value in #orders. Is there any way through which I can have the update statement execute after select. I tried below and couple of other options but no luck.
if #orders
#orders = List.select("itemname,tableno,quantity,itmstatus").where(:tableno => "01")
List.where(:tableno => "01").update_all(:ordstatus => 'displayed',:itmstatus => 'displayed')
end
Please advise. Thanks.
The thing is that the code #orders = List.select("itemname,tableno,quantity,itmstatus").where(:tableno => "01") is lazily evaluated when the view enumerates the #orders instance variable. That is, it's an ActiveRecord::Relation that only really gets evaluated (the SQL executed) at the time the view is rendered.
One way to prevent this—to fully execute the query and retrieve all the rows before the update statement later on is to call to_a on the ActiveRecord::Relation.
#orders = List.select(...).where(...).to_a
One thing to look out for is if you're using Kaminari for pagination then the regular Kaminari pagination extensions won't work—you'll have to use Kaminari::paginate_array.
Another thing to consider is if your query can potentially return a large number of records. By calling to_a you're telling ActiveRecord to retrieve all those records into memory all at once, which can degrade performance.
Note that, in Rails 3 it's also possible to use the .all method (as in, List.select().where().all) to execute and evaluate the query. However, in Rails 4, Model.all is now equivalent to Model.scoped and is lazily evaluated, hence, .to_a
Alternatively, you might want to look at the ActiveRecord::Relation#load method:
Causes the records to be loaded from the database if they have not been loaded already.
You can use this if for some reason you need to explicitly load some records before
actually using them. The return value is the relation itself, not the records.
Admittedly, I've never actually used that but it might be more appropriate in this case.

How to efficiently retrieve all record in rails 3.2 and assign them to a query-able object?

In our rails 3.2 app, we need to retrieve all customer records out of customer table and assign them to a variable customers and do query (such as .where(:active => true) on variable customers late on. There are 2 questions here:
what's the better way to retrieve all records?
Customer.all works. However according to rails document, it may have performance issue when Customer table gets large. We tried Customer.find_each and it has error "no block given (yield)".
How to make the variable customers query_able?
When performing query on variable customers (like customers.where(:active => true)), there is an error: undefined methodwhere' for #. It seems that thecustomersis an array object and can't takewhere. How can we retrievecustomers` in such a way it can be query-able?
Thanks for help.
In Rails < 4 .all makes database call immediately, loads records and returns array. Instead use "lazy" scoped method which returns chainable ActiveRecord::Relation object. E.g.:
customers = Customer.scoped
...
customers = customers.where(:active => true)
customers = customers.where(...)
etc...
And at the moment when you will need to load records and iterate over them you can call find_each:
customers.find_each do |customer|
...
end

Active Record - Get the second, third.. item in a database (without ID)

How to I retrieve the second, third .. entries in a database. I don't want to use the auto incrementing id generated by rails.
As I am deleting entries from my database, so the ID continues to increase.
So my DB would have
id : 210 (Even thought it's currently the first value)
id : 211 (Even thought it's currently the second value)
I know "Phone.first" will return the first how do I get the second.
Sub Question-
Destroy_all/delete_all - only removes the item, Can I remove all entries from the DB and have the DB id's start from one without dropping the table. As I ran into problems.
Say you want the fourth user:
#users = User.limit(4)
#fourth_user = #users[3]
Concerning your second question, destroy_all should do the trick since it triggers all callbacks. Be sure to add :dependent => :destroy in your relationships.
what you need is
#n=2,3,4 etc
.limit(1).offset(n)
Model.second was added to Rails 4.1.8.
So you can use it on Rails versions equal to or greater than that.
.third, .fourth and .fifth were also added to ActiveRecord::FinderMethods.

Resources