I'm writing a blog engine in rails, and have set up a tag model and a post model which have a have_and_belongs_to_many relationship. Tag addition works fine, the problem comes when looking for all posts with a specific tag:
If I add tag "test" to Post A, then add tag "test" to post B, there are two tag objects, both with name "test" but with different IDs, both referencing different posts. Now if I have a controller action indexTagPosts which takes the parameter "tag" and finds all posts with that tag, it will only return one post, since the other tag has a different ID and is not really associated. Should I be somehow restricting the addition of new tags, or should I be manipulating the way I pull all relevant tags differently?
Here is the controller action which is supposed to grab all relevant posts based on the parameter 'tag':
def indexTagPosts
@tag = Tag.find(params[:tag])
@posts = @tag.posts.all
end
And here is the action to save a tag:
def create
@post = Post.find(params[:post_id])
@tag = @post.tags.create(params[:tag])
respond_to do |format|
if @tag.save
format.html { redirect_to edit_post_path(@post),:notice => "Success" }
end
end
end
Thanks in advance and apologies for redundancy or bad wording.
I wish I knew where everyone got the idea to use has_and_belongs_to_many
because it's a really difficult thing to manage, even if it seems simple at the start. The better approach is to have a has_many ..., :through
type relationship because you can manage the individual links and add meta-data to them easily.
For instance, here is a simple two way join with an intermediate model, a pattern you'll find occurs quite often:
class Post < ActiveRecord::Base
has_many :post_tags
has_many :tags, :through => :post_tags
end
class Tag < ActiveRecord::Base
has_many :post_tags
has_many :posts, :through => :post_tags
end
class PostTag < ActiveRecord::Base
belongs_to :post
belongs_to :tag
end
Adding and removing Tag links at this point is trivial:
@post = Post.find(params[:post_id])
if (params[:tags].present?)
@post.tags = Tag.where(:name => params[:tags].split(/\s*,\s*/))
else
@post.tags = [ ]
end
The has_many
relationship manager will create, update, or destroy the PostTag
association models as required.
Generally you'll evolve the Post model to include a utility method for retrieving and assigning the tags using whatever separator you like:
class Post
def tags_used
self.tags.collect(&:name).join(',')
end
def tags_used=(list)
self.tags = list.present? ? Tag.where(:name => list.split(/\s*,\s*/)) : [ ]
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With