Reputation: 549
I am looking to setup a relationship where a user can create a post and tag that post. That post and those tags belong to that user.
I've been trying to debug this for the last three days. I don't know why when I save my Post, the user_id does not populate in the Tag model. I added the user_id into the Tag model.
I apologize in advance for the excessive code.
User - has_many posts; has_many tags
Post - belongs_to user; has_many taggings; has_many tags through taggings
Tag - has many taggings; has_many posts through taggings; belongs_to user
Tagging - belongs_to post; belongs_to tag
ActiveRecord::Schema.define(:version => 20121031012555) do
create_table "posts", :force => true do |t|
t.integer "user_id"
t.string "summary"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "taggings", :force => true do |t|
t.integer "post_id"
t.integer "tag_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "taggings", ["post_id"], :name => "index_taggings_on_post_id"
add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id"
create_table "tags", :force => true do |t|
t.string "name"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "users", :force => true do |t|
t.string "username"
t.string "email"
t.string "crypted_password"
t.string "password_salt"
t.string "persistence_token"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "role"
end
end
In the post Model:
def tag_ids=(tags_string)
self.taggings.destroy_all
tag_names = tags_string.split(",").collect{|s| s.strip.downcase}.uniq
tag_names.each do |tag_name|
tag = Tag.find_or_create_by_name(tag_name)
tagging = self.taggings.new
tagging.tag_id = tag.id
end
end
Upvotes: 0
Views: 156
Reputation: 11082
I apologize if I'm missing something, but it looks like you're not passing user id to the tag when it gets created. With rails 3 you can do your find_or_create_by for more than one attribute, like so:
tag = Tag.find_or_create_by_name_and_user_id(tag_name, user_id)
In addition, if you only want to build the Tag object, but not immediately save it to the DB, you may use the "find_or_initialize_by..." method, like so:
tag = Tag.find_or_initialize_by_name_and_user(tag_name, user)
Just make sure your Tag model has attr_accessible :user_id
if it doesn't already.
Update: Sorry I just re-read your question, and you stated that you do pass the user_id in to the tag model. However I don't see that in the code you provided; where does that occur?
Update #2 Based on the comment thread, your parameter map does not contain the user_id; this is, of course, because the parameter map is built from the form inputs, which apparently does not contain an input for the user.
I'm going under the assumption that when a post is created, it should be associated with the currently logged in user. I will also assume that you have, somewhere, available a reference to the currently logged in user, which I'm going to call @current_user.
Since it looks like you want to create the post and associated tags in one atomic operation, one thing you can do add the current user to a copy of the parameter map, like so:
post_params = params.clone.store(:user=>@current_user)
@post = Post.new(post_params)
There may be a better way of doing this, but this should work. Note that you probably don't need to make a clone of the parameter map; I admin I'm not sure what is best practice in this case.
Regardless, hope that helps.
update 3 Since a Ruby hash is iterated over in the order in which the keys are inserted, svn's tag_ids were being set BEFORE his user_id in to the Post model. In order to solve the problem, svn reversed the order of my previous suggestion, and ensured that the :user key was inserted before any other value. This is one possible solution (modified from svn's gist(https://gist.github.com/4027807)):
post_params = {:user => current_user}.merge!(params[:post].clone)
@post = Post.new(post_params)
Upvotes: 1
Reputation: 5974
before the line in your controller,
@post = Post.new(params[:post])
Add below line
@post = Post.new(:user_id => <assign your required user_id>)
This will make new post object to use user_id inside its instace method.
Then you will be able to use,below line successfully
tag = Tag.find_or_create_by_name_and_user_id(tag_name, user_id)
Hope it helps :)
Upvotes: 0