Reputation: 13835
Heres what I'm trying to accomplish:
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.
Upvotes: 6
Views: 30567
Reputation: 13835
The question I originally asked got pretty distorted by the end. So I'm separating it.
People who are trying to do what I originally asked can try this:
before_create :check_tag_exists
private
def check_tag_exists
@tag = Tag.find_by_name_and_user_id(self.name, self.user_id)
if @tag != nil
#
end
end
This will enable you to check if your record has already been created. Any further logic you can drop in that if statment.
Upvotes: 5
Reputation: 5362
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)
Upvotes: 9
Reputation: 18526
I believe the other answers are a bit dated. Here's how you should probably accomplish this for Rails 4
tag = Tag.first_or_initialize(:name => self.name, :user_id => current_user.id)
if !tag.new_record?
tag.id = self.id
tag.save
end
Upvotes: 3
Reputation: 17793
try this
def check_exists
tag = Tag.where(:name => self.name, :user_id => current_user.id).first
tag = Tag.new({:name => self.name, :user_id => current_user.id}) unless tag
end
use Tag.new
instead of Tag.create
Upvotes: 0
Reputation: 47548
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
Upvotes: 12
Reputation: 44932
You want to use the magic method find_or_create_by
def check_exists
tag = Tag.find_or_create_by_name_and_user_id(:name => self.name, :user_id => current_user.id)
end
Check out the ActiveRecord::Base docs for more info
Upvotes: 6