Rails difference in object created from a .find(:id) and .where() methods - ruby-on-rails

What is the difference in the objects created with these 2 methods:
tec = Technique.find(6)
tec2 = Technique.where(:korean => 'Jok Sul')
The data returned for each is exactly the same, yet the first object will respond perfectly to an inherited method like update_attributes while the second object will give an error of method not found.
When I do tec.class and tec2.class one is an ActiveRecord::Relation and the other doesn't give me a class at all, it just prints out the content of the object.
Maybe when you use the .where method you get an array, even if there is only one match and therefore you always have to issue the .each method to get at the contents? But that makes it hard to deal with when you want to update records, etc.
Can someone clarify this for me? Specifically, how to deal with matches found through the .where method.
Thanks.

Try:
tec2 = Technique.where(:korean => 'Jok Sul').first

Good question.
tec_scope = Technique.where(:korean => 'Jok Sul') # create an internal query
Remember, here only the query is created, it is not executed. You can programmatically build on top of this query if you so wished. The scope (or query if you so wish) will be executed in 2 ways. "Implicit" or "Explicit". Implicit way of running the query happens for example in the console, which invokes a method on the scope which automatically runs the query for you. This wont happen in your controllers unless you run it explicitly for .e.g
tec_scope.all # returns array
tec_scope.first # retuns one element
Scopes are just adding where clauses/predicates to your query. It's query building and delaying the execution till it is needed.
However,
tec_objects = Technique.find(6) # explicitly runs a query and returns one object (in this case)
This will explicitly run the query there and then. It is a question of the timing of execution of the query.
The difference is subtle but very important.
This hasnt got anything to do with whether you get one result or an array.
Technique.find([4,5]) # will return an array
Technique.find(4) # will return one object
Technique.where(:some_key => "some value").all # will return an array
Technique.where(:id => 5).first # will return one object
The difference is in timing of the execution of the query. Don't let the console fool you into believing there is no difference. Console is implicitly firing the query for you :)

The find(6) returns a single object, because you're specifying the object ID in the database, which is guaranteed to be unique by convention.
The where call returns a collection, which may be only 1 item long, but it still returns a collection, not a single object.
You can reveal this difference. Using your example code, if you call tec.class vs. tec2.class I think you'll find that they aren't the same class of object, as you expect.
That is, the methods available to a collection of objects is different than the methods available on an instance of that object.

Related

How to get active record's name after using find .where

Straight forward here:
<% #yyy = CityRace.where(city_race_id2: "3") %>
<% #xxx = #yyy.name %>
The #yyy is returning the proper record using the ID I have passed into it, but I'm trying to get the objects name. For some reason .name isn't working. Any idea what I'm going wrong here?
How do I find a record's name where id = a certain id?
where returns an ActiveRecord_Relationship, meaning an object containing CityRace objects for every row in the database with city_race_id2 equals to 3, no matter if there's only one, the result is an ActiveRecord_Relationship object, and that doesn't respond to name.
If you need the name of a particular object from that result, you can access to the specific element by its index and invoke name on it, e.g:
CityRace.where(city_race_id2: "3").first.name
Or to retrieve the name from every object:
CityRace.where(city_race_id2: "3").pluck(:name)
this one returns an array of strings, so, you must iterate over them to print them all or get a specific one.
In the other hand if you need a single row from the query, use find_by:
CityRace.find_by(city_race_id2: "3").name
.where returns an ActiveRecord::Relation which behaves like an array. You can think of it like a special kind of array that allows you to chain on more active record queries on it. When you call certain methods like each or to_a it evaluates the query into an actual array.
In any case, what you are looking for here is not something array-like. You want #yyy to refer to a single record.
Simple fix, just use find_by instead of where. Also take a look at https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find and find vs find_by vs where

Returning the original relation in a class method called on a relation

I have a class method on a model called order_by_ids(ids) that is called by an ActiveRecord Relation. Below is an example of its usage:
User.where(id: [1,2,3]).order_by_ids([2,1,3])
...will return Users 1, 2, and 3 in the order of: [2,1,3]
I would like for it to return the original relation (essentially doing nothing) if passed an empty array.
The following returns the entire class, not just the relation it's called on:
return self unless ids.present?
The following works 100%, but it seems inelegant. Also, I think it runs an unnecessary query (seems slower in the console anyway):
return where.not(id: nil) unless ids.present?
Is there a quick way to just return the relation it's called on? I could theoretically make it a scope, but I've been taught to avoid scopes with arguments (see this guide for reference).
Note: I am using Rails 3, so all returns an array. I'm essentially looking for a Rails 4 version of all.
The following should preserve the upstream scope chain (returning all, not self):
return all if ids.none?
P.S. Named scopes is a perfectly accepted and conventional way of dealing with queries.

Ruby on Rails where()

I'm doing my ruby project but i got a problem.
I have a problem on where in ruby like below.
#talk = Talk.where(params[:ask])
I get
"#<Talk::ActiveRecord_Relation:0x007f811404dae8>"
this from #talk.
I'm finding the value of the answer in my db with ask.
How can I print the value of the talk with where()?
If you have distinct ask values in your talk objects then you can do
#talk = Talk.find_by(ask: params[:ask])
which will return a single talk object. If no such ask value can be found nil is returned.
The objects are only retrieved when special methods like first, each etc are called. This is called lazy loading.
#talk = Talk.where(params[:ask])
#talk = Talk.where(params[:ask]).first
This way you will get the first entry which will be returned by your where, you can read some more about the lazy loading vs eager loading on this thread
where() will return a collection object. You are missing a column name with it.
If you want to access one object from the collection, use
#talk = Talk.where(column_name: params[:ask]).first #Or Talk.where(column_name: params[:ask]).last depending on the requirement
If you want to access all objects, you will have to loop through every object returned,
#talks = Talk.where(column_name: params[:ask])
-unless #talk.nil?
#talks.each do |talk|
= talk.column_name

Scope chaining with optionally parameters

currently I have the following type of code
scope :scope1, lambda{|param| where(param1: params)}
scope :scope2, lambda{|param| where(param2: params)}
and in the controller I would do
results = Model.some_other_scope
if !params[:param1].blank?
results = results.scope1(params[:param1])
end
if !params[:param2].blank?
results = results.scope2(params[:param2])
end
this is not very efficient since it does several calls to DB instead of one.
Is there a way to chain these scopes so it will be disabled if the params don't exist?
results = Model.scope1(params[:param1]).scope2(params[:param2])
currently, chaining doesn't work because of the where clause doesn't return anything then the whole chain breaks
Actually, ActiveRecord queries are evaluated lazily, so, simplyfying, this code produces only one DB call. So your problem doesn't really exist.

proxy_reflection equivalent on ActiveRecord::Base#find?

I'm trying to make a helper which automatically picks the correct partial based on the types of objects returned by either ActiveRecord::Base#find or an association. Unfortuneatly I can't just look at the first element of the returned array because I want to pick the correct one in this case as well. If you call an association, it returns a proxy with the proxy_reflection method, which is exactly what I want, but it doesn't exist on the result of ActiveRecord::Base#find :(.
Example:
association_posts = Author.find(1).posts
association_posts.proxy_reflection.class_name # Returns "Post"
all_posts = Post.find(:all)
all_posts.proxy_reflection # no method exception, what do I call here instead?
I'm not sure if this is what you want, but try:
all_posts.first.class

Resources