Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check if a record exists before creating a new one in rails3?

Heres what I'm trying to accomplish:

  • I have a tagging system in place.
  • Tags are created, when Posts are created (posts has_many :tags, :through => :tag_joins.
  • A tag join is automatically created when a post is created with tags).

I want to check if the tag already exists. If it does I want to use the existing tag for the tag_join record, rather than creating a new tag record.

Here is my current code, which isn't working.

class Tag < ActiveRecord :: Base
  belongs_to :user
  belongs_to :tag_join
  belongs_to :post

  before_create :check_exists

  def check_exists
    tag = Tag.where(:name => self.name, :user_id => current_user.id)
    if tag.nil?
      tag = Tag.create(:name => self.name, :user_id => current_user.id)
    end
  end

end

This doesn't work though, I'm getting an error upon task creation...(the server is actually just timing out - I don't receive a specific error).

Any ideas?

Tokland said I was creating an infinite loop by telling it to create tag again - so I tried this:

 def check_exists
      tag = Tag.find_by_name_and_user_id(:name => self.name, :user_id => current_user.id)
      if tag != nil
        self.id = tag.id
      end
  end

And still get the server timeout

Edit: I'm not sure if this matters, but the way the tags are being added is similar to "http://railscasts.com/episodes/73-complex-forms-part-1

they're nested in the post form, and use something like this:

def tag_attributes=(tag_attributes)
  tag_attributes.each do |attributes|
    tags.build(attributes)
  end
end

I'm wondering if this is stopping this whole thing from working? Also, using current_user.id in the model definitely seems to be an issue...

EDIT:

Something I have figured out: this had to change, the format we were using before was incorrect syntax - generally used for a .where method.

  def check_exists
     @tag = Tag.find_by_name_and_user_id(self.name, self.user_id) 
     if @tag != nil
       #return false
       #self=@tag
     end
  end

The problem now is this, I can learn if it the tag already exists. But then what? If I go with the return false option, there is an error upon post creation, and the join record isn't created... The other option "self=@tag" obviously just doesn't work.

like image 790
Elliot Avatar asked Feb 12 '11 15:02

Elliot


People also ask

What is rails validation?

Rails validation defines valid states for each of your Active Record model classes. They are used to ensure that only valid details are entered into your database. Rails make it easy to add validations to your model classes and allows you to create your own validation methods as well.


2 Answers

You're going to find it hard to to this from within the Tag model. It seems like what you want is to update the Post using nested attributes, like so:

post = Post.create
post.update_attributes(:tags_attributes=>{"0"=>{:name=>"fish",:user_id=>"37"}})

This is actually pretty simple to do by using a virtual attribute setter method:

class Post < AR::Base
  has_many :tags

  def tags_attributes=(hash)
    hash.each do |sequence,tag_values|
      tags <<  Tag.find_or_create_by_name_and_user_id(tag_values[:name],\
        tag_values[:user_id])
    end
  end

> post = Post.create
> post.update_attributes(:tags_attributes=>{"0"=>{:name=>"fish",:user_id=>"37"}})
> Tag.count # => 1
# updating again does not add dups
> post.update_attributes(:tags_attributes=>{"0"=>{:name=>"fish",:user_id=>"37"}})
> Tag.count # => 1
like image 152
zetetic Avatar answered Sep 28 '22 07:09

zetetic


There's a find_or_create_by_ function built right in to Rails

# No 'Summer' tag exists
Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")

# Now the 'Summer' tag does exist
Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")

http://api.rubyonrails.org/classes/ActiveRecord/Base.html (under Dynamic attribute-based finders)

like image 26
Bjorn Avatar answered Sep 28 '22 07:09

Bjorn