0
votes

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
2
You could use has_and_belongs_to_many relation for that. That helps you to avoid that dummy destroy.ck3g
You are not save taggings after assigning tagging.tag_id = tag.id. And I don't see where you trying to save user_idck3g
@ck3g This is the gist he made for this: gist.github.com/4025471Paul Richter
@ck3g HABTM is just a simpler many to many association. It should be ok.1dolinski

2 Answers

1
votes

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)
0
votes

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 :)