0

In my ruby model I have a has_and_belongs_to_many relation with a model "search":

has_and_belongs_to_many :searches

I want to add a new search object only if it does not already exists, so I wrote:

def append_unless_already_there search
  unless searches.exists?(search)
    searches << search
end

However, adding 2 times a new object passes the condition, and results in a sql exception. Tried replacing search with search.id, but does not change anything. I added some logging to the code:

def append_unless_already_there search
  puts ""
  puts searches.exists?(search)
  unless searches.exists?(search)
    puts "["
    searches.each do |s|
      puts s.id
    end
    puts "]"
    puts search.id

    searches << search
  end
end

Adding 5 search objects, of which the last 2 are equal, results in the following logging when adding the last object:

false
[
12
5
8
1
]
1

As I read the documentation and examples given, this last exists? check should return true. I cannot figure out what I am missing here. Thanks in advance.

Jos
  • 1,015
  • 2
  • 13
  • 25

2 Answers2

2

exists? doesn't take an ActiveRecord::Base object, it takes an ID (or list of IDs etc, see the API Docs for more information.

So to test for existence you want to do:

searches.exists?(search.id)

rather than:

searches.exists?(search)
Chris Bailey
  • 4,126
  • 24
  • 28
  • Thanks for your quick response and help Chris. However, I did already try that before posting (inspired by the example in the API you linked). After reading your answer I tried again a few times, but the same happens. Logging is also exaclty equal, whether using "search" or "search.id". – Jos Apr 05 '12 at 10:07
  • Hmm interesting. Just tested that myself, seems that exists? will take a Model object. You learn something new everyday! Have you taken a look at the SQL queries that are being generated to make sure they're doing what you'd expect? – Chris Bailey Apr 05 '12 at 10:39
  • Did not do that. Tried to clearify using step filters in debugging, but did not find anything yet. As Vik says in next post, include? does the job. Still interesting why exists? does not. Thanks for your help. – Jos Apr 05 '12 at 10:53
1

Try:

(searches << search) unless searches.include?(search)

OR

(searches << search) if !searches.include?(search)

EDITED

I am not sure 100% but in case of exists it takes id or some value :

exists?(id = false) 

but in case of include , it takes key or object as well :

include?(key) 

For more detail check on http://api.rubyonrails.org/

Vik
  • 5,931
  • 3
  • 31
  • 38
  • Yes. This does the trick. Thanks! Still curious why exists? did not work, while it should do it according API. Thanks a lot. – Jos Apr 05 '12 at 10:51
  • mm, tried searches.exists?(id = search.id) and added puts searches.exists?(id = search.id) at the first line of the method. This logs 5 times false (while 5th should be true) and throws the ConstraintException. – Jos Apr 05 '12 at 11:11
  • don't try : searches.exists?(id = search.id), simply use searches.exists?(search.id) as Chris said . – Vik Apr 05 '12 at 11:16
  • Ah, I did try that, before posting and again after Chris' response, but it did not do the trick. – Jos Apr 05 '12 at 12:24