103

In my app a User can create a Business. When they trigger the index action in my BusinessesController I want to check if a Business is related to the current_user.id:

  • If yes: display the business.
  • If no: redirect to the new action.

I was trying to use this:

if Business.where(:user_id => current_user.id) == nil # no business found end 

But it always returns true even when the business doesn't exist...

How can I test if a record exists in my database?

3
  • 1
    Using where will return an empty array if there are no records. And [] doesn't equal nil Commented May 22, 2013 at 3:01
  • What about just a unless Business.find_by_user_id(current_user.id)? Commented May 22, 2013 at 3:14
  • possible duplicate of Checking if ActiveRecord find returns a result Commented Jun 25, 2014 at 14:21

8 Answers 8

258

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
Sign up to request clarification or add additional context in comments.

6 Comments

That didn't seem to work. It keeps passing that test and loading the index html like with the == nil test (so I'm getting an error: undefined method `name' for nil:NilClass).
Try with first before calling present
Oh that's my fault I was confuse, you need to test with blank?
Thanks that worked! I shouldn't have noticed that mistake. Could you tell me why the == nil check didn't work?
Yes give me a minute I will update my answer, also don't use the .first before .blank? ;)
|
30

In this case I like to use the exists? method provided by ActiveRecord:

Business.exists? user_id: current_user.id 

1 Comment

Is exists? with OR possible?
6

with 'exists?':

Business.exists? user_id: current_user.id #=> 1 or nil 

with 'any?':

Business.where(:user_id => current_user.id).any? #=> true or false 

If you use something with .where, be sure to avoid trouble with scopes and better use .unscoped

Business.unscoped.where(:user_id => current_user.id).any? 

1 Comment

Better use Business.unscoped.where(:user_id => current_user.id).pluck(:id).any? to avoid unnecesary load of relations for the object you are checking.
1

ActiveRecord#where will return an ActiveRecord::Relation object (which will never be nil). Try using .empty? on the relation to test if it will return any records.

Comments

1

When you call Business.where(:user_id => current_user.id) you will get an array. This Array may have no objects or one or many objects in it, but it won't be null. Thus the check == nil will never be true.

You can try the following:

if Business.where(:user_id => current_user.id).count == 0 

So you check the number of elements in the array and compare them to zero.

or you can try:

if Business.find_by_user_id(current_user.id).nil? 

this will return one or nil.

Comments

1
business = Business.where(:user_id => current_user.id).first if business.nil? # no business found else # business.ceo = "me" end 

Comments

0

I would do it this way if you needed an instance variable of the object to work with:

if @business = Business.where(:user_id => current_user.id).first #Do stuff else #Do stuff end 

Comments

0

Something new to try (:

Assign a variable or return

return unless @business = Business.where(user_id: current_user.id).first 

Method would exit at this point if there are no businesses found with current user's ID, or assigns instance variable @business to the first business object.

Comments