Checking if ActiveRecord find returns a result
find :all
returns an empty array ([]
) if no rows are returned, so you can just use it this way:
post = Post.find(:all, :conditions => { :url => params['url'] }, :limit => 1)
unless post.empty?
# do something...
end
By the way, if you do find :all
you're going to get an array, not a single row. If you're trying to get just one Post, it would be cleaner to use the find_by
helper or find :first
or just first
instead:
post = Post.find_by_url params['url']
# or
post = Post.first :conditions => { :url => params['url'] }
# then...
if post
# do something...
end
ruby-on-rails check if query result is empty (Model.find)
Use this to check for nil as well as empty cases:
@search.blank?
For the opposite case (NOT nil and NOT empty), use .present?:
@search.present? #equivalent of !@search.blank?
The reason your query returned nil
instead of empty
is :
Customer.find_by_name($login_name)
always returns one result of object Customer
or nil
if there is no such result,
while something like:
Customer.where(:name=>$login_name)
will return you ActiveRecord::Relation
(which can have 0-n results) always. and empty?
method will work on it
Check if record exists from controller in Rails
Why your code does not work?
The where
method returns an ActiveRecord::Relation object (acts like an array which contains the results of the where
), it can be empty but it will never be nil
.
Business.where(id: -1)
#=> returns an empty ActiveRecord::Relation ( similar to an array )
Business.where(id: -1).nil? # ( similar to == nil? )
#=> returns false
Business.where(id: -1).empty? # test if the array is empty ( similar to .blank? )
#=> returns true
How to test if at least one record exists?
Option 1: Using .exists?
if Business.exists?(user_id: current_user.id)
# same as Business.where(user_id: current_user.id).exists?
# ...
else
# ...
end
Option 2: Using .present?
(or .blank?
, the opposite of .present?
)
if Business.where(:user_id => current_user.id).present?
# less efficiant than using .exists? (see generated SQL for .exists? vs .present?)
else
# ...
end
Option 3: Variable assignment in the if statement
if business = Business.where(:user_id => current_user.id).first
business.do_some_stuff
else
# do something else
end
This option can be considered a code smell by some linters (Rubocop for example).
Option 3b: Variable assignment
business = Business.where(user_id: current_user.id).first
if business
# ...
else
# ...
end
You can also use .find_by_user_id(current_user.id)
instead of .where(...).first
Best option:
- If you don't use the
Business
object(s): Option 1 - If you need to use the
Business
object(s): Option 3
Rails - Check if query returns no results
Given the following example models:
# rails g model country name
class Country < ApplicationRecord
has_many :states
has_many :cities, through: :states
end
# rails g model country name country:belongs_to
class State < ApplicationRecord
belongs_to :country
has_many :cities
end
# rails g model city name state:belongs_to
class City < ApplicationRecord
belongs_to :state
has_one :country, through: :state
end
We can get records with matches in joined tables by applying an INNER JOIN:
countries_with_states = Country.joins(:states)
countries_with_cities = Country.joins(states: :cities)
# can be shortened since we have an indirect association
countries_with_cities = Country.joins(:cities)
This will just returns rows with at least one match in the joined table.
We can also get records without matches in the joined table by using a LEFT OUTER JOIN with a condition on the joined table:
countries_with_no_states = Country.left_joins(:states)
.where(states: { id: nil })
countries_with_no_cities = Country.left_joins(:cities)
.where(cities: { id: nil })
Using #map
on an association should not be done as its extremely ineffective and can lead to serious performance issues. You instead need to create meaningful associations between your models and use joins to filter the rows in the database.
ActiveSupport's #blank?
and #empty?
methods should really only be used when dealing with user input like strings and arrays of strings which is where its heuristics are actually useful.
For collections of records you have .exists?
which will always create a query and .any?
and .none?
which will use the size if the relation has been loaded.
Active Record `find` returns nil when a valid record exists
So I found the answer. The problem was with the link in my test: expect(Article).to receive(:find).with(article.id.to_s)
. Without stating what I want Article.find(params[:id)
to return, it returns nil
. Hence no error message was received and `find_by(id: params[:id]) worked.
Active Record find_by returns null
I believe you should permit the params, I downloaded your repo and I was able to get the desired query by permitting the params, like this:
class Api::V1::UsersController < ApplicationController
def index
@users = User.find_by(user_params)
respond_to do |format|
format.json { render json: @users.to_json, status: :ok }
end
end
private
def user_params
params.permit(:first_name, :last_name, :email, :city, :state)
end
end
Now if you try this query http://localhost:3000/api/v1/users?first_name=Tyrone&last_name=Slothrop&city=Chicago
you should see on the logs that the only the params that you passed are being used for the search:
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."first_name" = ? AND "users"."last_name" = ? AND "users"."email" = ? AND "users"."city" = ? LIMIT ? [["first_name", "Tyrone"], ["last_name", "Slothrop"], ["email", "jean@email.com"], ["city", "Chicago"], ["LIMIT", 1]]
↳ app/controllers/api/v1/users_controller.rb:11
Rails is just permitting the params defined on the user_params, so if they're not present, it will just be ignored
Edit: TO return the list of matches you should use where:
def index
@users = User.where(user_params)
respond_to do |format|
format.json { render json: @users.to_json, status: :ok }
end
end
To allow multiple parameters, for example to allow for multiple first_name fields you can do:
def user_params
params.permit(:last_name, :email, :city, :state, first_name: [])
end
Remember to put your array items in the end of the permit to avoid syntax errors, and after that you can query the first name like this: http://localhost:3000/api/v1/users?first_name[]=John&first_name[]=Jean
,
That url can get pretty tedious to build, so I would suggest that if you are building an api you can always send the params in the body of the request, like this:
{
"first_name": ["John", "Jean"]
}
And that will have the exact behavior of passing the params in the url.
If you strictly want to send the id like id=1,2,3 that you can do something like:
class Api::V1::UsersController < ApplicationController
def index
ids = params[:id].split(",")
@users = User.where(user_params.merge!(id: ids))
respond_to do |format|
format.json { render json: @users.to_json, status: :ok }
end
end
private
def user_params
params.permit(:last_name, :email, :city, :state, :first_name)
end
end
The user_params.merge! will do the trick since it will overwrite or add the id key to your user_params hash.
`detect` and `find` return `nil` whereas `find_all` and `select` return a result
There are two different issues:
1) Why the version with byebug
doesn't work
This is the important piece from the docs of find
: Returns the first for which block is not false.
Now let's have a look at your cases:
# just writing "true" here, with no if statement will deliver a result
If you just write true
at the end of the block then that true
is returned and therefore find
finds this entry.
if (event.end_time > instance.start_time && event.end_time <= instance_end_time && instance.events.first.period == event.period)
ok = true
end
ok
This case is similar: If the if
condition is true
you assign true
to the ok
variable. Because you call ok
again in the last line of the block the block returns true
and find
finds this element.
if (event.end_time > instance.start_time && event.end_time <= instance_end_time && instance.events.first.period == event.period)
byebug
end
But this example in your code is different. Here you open bundle
if the if
condition is true
. This makes the bundler
call the last method call in the find
block. Calling bundler
doesn't return true
therefore the whole block doesn't return true
and find
doesn't pick these entry.
The solution is to return true
in the last line of the find
block. Because you have already the condition in your code, you could use that directly without assigning true
to a variable first.
– for example like this:
cspg_instance = @game_instances.find do |instance|
instance_end_time = TimeOperation.new(:+, instance.start_time, instance.duration).result
event.end_time > instance.start_time && event.end_time <= instance_end_time && instance.events.first.period == event.period
end
2) Why does find_all
and select
work, but find
does not?
In the comments, you clarified that @game_instances
is actually not an Array, but an ActiveRecord::Relation
. ActiveRecord::Relation#find
works totally different than the method on an Array
. Simplified find
on such a relation expects an id
of a record and returns that record within the scope given by the relation. Calling to_a
on the relation loads all records into memory an allows you to use Array#find
.
From a performance point of view it would make sense to translate the condition into a SQL condition and only load the one record that matches from the database instead of loading all records and find the correct one in your application.
Rails Find a Record Within ActiveRecord::Relation Object without querying database again
Once the relation has been loaded, you can use regular array methods. find
is actually a very interesting method here - if block is specified, it will be delegated to the relation target:
@blogs.find {|b| b.id == 1}
Rails checking if a record exists in database
If you want to check for the existence of an object why not use exists?
if Truck.exists?(10)
# your truck exists in the database
else
# the truck doesn't exist
end
The exists?
method has the advantage that is not selecting the record from the database (meaning is faster than selecting the record).
The query looks like:
SELECT 1 FROM trucks where trucks.id = 10
You can find more examples in the Rails documentation for #exists?.
Related Topics
Initializing Instance Variable as an Array - Ruby
Ruby, How to Add a Param to an Url That You Don't Know If It Has Any Other Param Already
How to Upload a Local File to a Carrierwave Model
How to Send Message to All Client Except Sender in Rails/Actioncable
Application.CSS Not Being Served as an Asset
Os X Mavericks Install Rvm Warning
Ruby Gem Installation Error After Osx Yosemite and Xcode 6 Installation
In Ruby, How to Remove Only 1 Match in an Array Easily
How to Bundle Install Gemfile with Specific Version of Bundler
Access Slack Files from a Slack Bot
What's the Best Way to Start a Background Process, That Can Get Accessed Later On
How to Store a Ruby Array into a File
How to Best Create a Random Float in a Range Between Two Floats
Best Practice for Limiting the Number of Associations Within a Has_Many Relationship