What is returned by calling 'where'? - ruby-on-rails

I have following question, let
#post=Post.where(title: "title1")
what should be #post if "title1" exists or not? How can I check if 'where' returned any element?
In my application I just called
if(#post) .... else .... end
but it always get into else

Try:
#post=Post.where(title: "title1").first
Then #post would be either a Post object with title = "title1" or nil.
.where returns a ActiveRecord_Relation object, which is the result of filtering the current relation according to the conditions in the arguments.
An alternative is to use find_by as suggested by #D-side in the comments:
#post = Post.find_by(title: "title1")

Related

Problem with selecting elements with the same params

What i do wrong? I want to return every products which pass condition:
def show
products = Product.select{|x| x[:category_id] == params[:id]}
render json: products
end
When i write
def show
products = Product.select{|x| x[:category_id] == 1}
render json: products
end
it works why the first example doesn't work?
I am pretty sure that there is mismatch in data type.
1=='1' #will be always false
1==1 #will be true
'1'=='1' #will be true as well
And also check for nil value from params[:id]
Please make sure to change as follows
def show
products = Product.select{|x| x.category_id == params[:id].to_i}
render json: products
end
OR
The best solution as suggested by #Eyeslandic is to use .where as it will not check for mismatch in data type. And also you don't have to take care of nil value from params[:id].
You should really be using a where to stop sql from loading all your products.
#products = Product.where('category_is = ?', params[:id])
The being said, if you are sticking to rails restful conventions, the fact you have a param called :id that is the category_id suggests you are on the category controller. So maybe consider changing your logic to:
#category = Category.includes(:products).find(params[:id])
you can then access products via
#category.products
or if your not interested in the category too much maybe
#products = Category.includes(:products).find(params[:id])&.products

Ruby on Rails beginner question : equality

I'm starting to know ROR and I was doing a kind of blog with articles, etc...
I did this code :
def show
id = params[:id]
list = Article.all
is_valid = false
list.all.each do |article|
if article.id == id
#is_valid = true
break
end
end
As you can see, this code just wants to check if the article ID exists or not. So I'm testing equality between id and article.id (which's a model linked to the appropriated table in the database) BUT when I try to use or display #is_valid boolean I saw that article.id == id is FALSE every time, even if article.id = 2 and id = 2. I tried to think about everything that can make this occuring, but I admit I still misunderstand this.
Then I ask you if you know why this is occuring. Of course, an equality like 2 == 2 will change #is_valid to true.
Thank you for your help !
Maybe its because params[:id] it's a string and article.id it's an Integer
(byebug) params
{"controller"=>"admin/my_controller", "action"=>"edit", "id"=>"1"}
And yes it is... "id" is a string "1", so you may try this:
def show
id = params[:id].to_i
list = Article.all
is_valid = false
list.all.each do |article|
if article.id == id
#is_valid = true
break
end
end
end
And maybe could work.
This is the answer to your question,
But if you want to learn a little more about Activerecord you can do this
Article.exists?(params[:id])
and that will do what you are trying to do just with a query against db.
and if you want to get just a simple article
record = Article.find_by(id: params[:id]) #return nil when not exist
if record # if nil will threat like false on ruby
#my code when exist
else
#my code when not exist
end
will work (you also can use find but find will throw an exception ActiveRecord::RecordNotFound when not exists so you have to catch that exception.
Activerecord has many ways to check this you dont need to do it by hand.
def show
#article = Article.find(params[:id])
end
This will create a database query which returns a single row. .find raises a ActiveRecord::NotFound exception if the record is not found. Rails catches this error and shows a 404 page. Article.find_by(id: params[:id]) is the "safe" alternative that does not raise.
Your code is problematic since list = Article.all will load all the records out of the database which is slow and will exhaust the memory on the server if you have enough articles. Its the least effective way possible to solve the task.
If you want to just test for existence use .exists? or .any?. This creates a COUNT query instead of selecting the rows.
Article.where(title: 'Hello World').exists?

How can I set a variable to a value, only if that value is valid?

I want to set a variable to a given value, but only if the value is valid.
Right now this is the code I have:
if Something.find(params[:id].comments.first.exists?
#comment = Something.find(params[:id]).comments.first
else
#comment = nil
end
But this is inefficient because it has to load the records twice.
I tried to use the ruby method try to ensure that the variable would only be set if the value is valid:
#comment = Something.try.find(params[:id]).comments.first
but no matter where I put it, I get back a "nil is not a symbol" error. It seems try is only for printing variables.
Anyone know how else I can accomplish this with only one query?
You could try:
#comment = Comment.find_by(something_id: params[:id])
Did you tried?
if #comment = Something.find(params[:id]).comments.first
# do something with your #comment variable
else
# do something else
end
More examples: Check if record exists from controller in Rails
# Attempt your query
#comment = Something.find(params[:id]).comments.first
# If the record does not exist, use rescue to handle the exception gracefully
rescue ActiveRecord::RecordNotFound
# Handle the exception
#comment = nil
end
You can find more information on exceptions and exception handling here.
I think this is what you want:
#comment = Something.find_by(id: params[:id]).try(:comments).try(:first)
In this case find_by will return a Something object or nil. In case of nil, the chain of tries will still return nil, and, in case of object, methods in tries will be executed and give you the first comment, if such exists (note! you might still get a nil if object exists because that object might not have any comments).
Thus, you will set your #comment only if Something object exists and it has comments; otherwise, it will be nil.

Lookup by slug and id

I currently have the following in my products controller show action:
#product = Spree::Product.active.where(slug: params[:id]).first!
This only works when slug is passed, I want to be able to pass in id or slug and for it to work:
kinda like:
Spree::Product.active.where(slug: params[:id]).first! || Spree::Product.active.where(id: params[:id]).first!
Right now when I type products/foo-bar it works, not when I type products/1
Is there a way to do this? Thank you in advance.
.first!
Raises error when record not found.
Try
Spree::Product.active.find_by(slug: params[:id]) || Spree::Product.active.find_by(id: params[:id])
It'll try second query if first does not return value
The simplest way would be to search for both in a where like so:
Spree::Product.active.where("slug = :parameter OR id = :parameter", parameter: params[:id]).first!
This will find the Spree::Product where either the slug or id is equal to the params[:id].

Referencing Model with String input

Lets say I wish to make a page that can query the desired object with type(string) and id(int).
/query?type=people&id=1
would fetch me
Person.find(1)
whereas
/query?type=cities&id=123
would fetch me
City.find(123)
However, I have problems as to how to translate strings into the desired model class.
The only way I can think of is
case params[:type]
when 'people'
#object = Person.find(params[:id])
when 'cities'
#object = City.find(params[:id])
end
However, this method will be quite problematic if I have more types of models.
Is there a better way?
Thank you in advance,
Try:
klass = params[:type]
klass.singularize.classify.constantize.find(params[:id])
Edit:
#object = params[:type].singularize.classify.constantize.find(params[:id])

Resources