Polymorphic Problem

Hi. I believe that I need to write a method to get my user's information, but I'm not sure how to write this and where it should go.

I have Reviews as polymorphic and there are several types items that can be reviewed (books, music, movies, etc). A user can search for a book and see all of the reviews for that book. Posted with each review, I want to provide the name of the user. Based on what I have, what is the best way to do this and where does this code go? Right now if I pull up the book, I can see all of the reviews. In the review table I have the review_user_id and now I want to display the name of that user from the users table.

Here's my basic structure, but noting that I have put snippets of the data here for brevity.

class CreateReviews < ActiveRecord::Migration   def self.up     create_table :reviews       t.string :reviewable_type       t.integer :reviewable_id       t.string :title       t.text :body       t.integer :review_user_id     end   end end

class CreateUsers < ActiveRecord::Migration   def self.up     create_table :users       t.string :name     end   end end

class Review < ActiveRecord::Base   belongs_to :reviewable, :polymorphic => true end

class Book < ActiveRecord::Base   has_many :reviews, :as => reviewable end

class User < ActiveRecord::Base   has_many :reviews, :as => :reviewable end

THANKS!

Please see the above post for the setup of my models.

While trying to figure this out, I put this into script/console:

Book.find(1).reviews

=> [<#Review id: 2, reviewable_type: "Book", reviewable_id: 1, review_user_id: 1>]

How can I take the value of review_user_id and get the name of the user in the users table?

Thanks!

If your review model looks like this:

class Review   belongs_to :user   belongs_to :reviewable, :polymorphic => true end

You could probably do something like this:

Book.find(1).reviews.each do |r|   puts r.user.name end

You need to let ActiveRecord know about the association: (You don't specify the Rails version you're using, but I'm guessing 2.x)

class Review < ActiveRecord::Base   belongs_to :reviewable, :polymorphic => true   belongs_to :review_user, :class_name => 'User'   # or perhaps:   # belongs_to :reviewer, :class_name => 'User', :foreign_key => 'review_user_id' end

class Book < ActiveRecord::Base   has_many :reviews, :as => reviewable end

class User < ActiveRecord::Base   has_many :reviews, :as => :reviewable # can users be reviewed like Books, etc.?

  # if you mean for user.reviews to be the set of reviews   # that this user wrote, then you want:   has_many :reviews, :foreign_key => 'review_user_id' end

Does that work for you?

-Rob

Rob Biedenharn http://agileconsultingllc.com Rob@AgileConsultingLLC.com

You need to let ActiveRecord know about the association: (You don't specify the Rails version you're using, but I'm guessing 2.x)

Yes, using Rails 2.1

class User < ActiveRecord::Base   has_many :reviews, :as => :reviewable # can users be reviewed like Books, etc.?

  # if you mean for user.reviews to be the set of reviews   # that this user wrote, then you want:   has_many :reviews, :foreign_key => 'review_user_id' end

In my reviews table I have 2 user values, created_by and modifed_by. Users can not be reviewed, but you can view all of the reviews per user. Can I do the foreign key like you have in your second example if my review table has 2 fields that link back to the user.id field?

Then you need to give the proper foreign_key

class User    has_many :reviews, :foreign_key => 'created_by' end

class Review    belongs_to :creator, :class_name => 'User', :foreign_key => 'created_by' end

-Rob

Rob Biedenharn http://agileconsultingllc.com Rob@AgileConsultingLLC.com

Rob Biedenharn wrote:

Then you need to give the proper foreign_key

class User    has_many :reviews, :foreign_key => 'created_by' end

class Review    belongs_to :creator, :class_name => 'User', :foreign_key => 'created_by' end

I must be missing what you're telling me because I get an error. Polymorphic associations are new to me and quite confusing. I've read a lot, but still don't quite understand how to set these up.

C:/project/vendor/rails/activesupport/lib/active_support/dependencies.rb:2 15:in `load_without_new_constant_marking': C:/var/intranet3/app/models/user.rb:1 55: parse error, unexpected kEND, expecting $ (SyntaxError)

class CreateReviews < ActiveRecord::Migration   def self.up     create_table :reviews       t.string :reviewable_type       t.integer :reviewable_id       t.string :title       t.text :body       t.integer :create_review_user_id       t.integer :modify_review_user_id     end   end end

class User     has_many :reviews, :foreign_key => 'create_review_user_id' has_many :reviews, :foreign_key => 'modify_review_user_id' end

class Review     belongs_to :creator, :class_name => 'User', :foreign_key => 'create_review_user_id' belongs_to :modified, :class_name => 'User', :foreign_key => 'modify_review_user_id' end

Rob Biedenharn wrote:

Then you need to give the proper foreign_key

class User   has_many :reviews, :foreign_key => 'created_by' end

class Review   belongs_to :creator, :class_name => 'User', :foreign_key => 'created_by' end

I must be missing what you're telling me because I get an error. Polymorphic associations are new to me and quite confusing. I've read a lot, but still don't quite understand how to set these up.

C:/project/vendor/rails/activesupport/lib/active_support/dependencies.rb:2 15:in `load_without_new_constant_marking': C:/var/intranet3/app/models/user.rb:1 55: parse error, unexpected kEND, expecting $ (SyntaxError)

class CreateReviews < ActiveRecord::Migration def self.up    create_table :reviews

missing: do |t|

     t.string :reviewable_type      t.integer :reviewable_id      t.string :title      t.text :body      t.integer :create_review_user_id      t.integer :modify_review_user_id    end end end

class User    has_many :reviews, :foreign_key => 'create_review_user_id' has_many :reviews, :foreign_key => 'modify_review_user_id'

You can't call both associations "reviews" has_many :created_reviews, ... has_many :editted_reviews, ...

end

class Review    belongs_to :creator, :class_name => 'User', :foreign_key => 'create_review_user_id' belongs_to :modified, :class_name => 'User', :foreign_key => 'modify_review_user_id'

belongs_to :modifier might be a better name than 'modifiED'

end

-Rob

Rob Biedenharn http://agileconsultingllc.com Rob@AgileConsultingLLC.com

Ok, here's what I have in my models.

class Review < ActiveRecord::Base   belongs_to :reviewable, :polymorphic => true   belongs_to :created_reviews, :class_name => 'User', :foreign_key => 'create_review_user_id'   belongs_to :modified_reviews, :class_name => 'User', :foreign_key => 'modify_review_user_id' end

class User < ActiveRecord::Base   has_many :reviews, :as => :reviewable   has_many :created_reviews, :foreign_key => 'create_review_user_id'   has_many :modified_reviews, :foreign_key => 'create_review_user_id'

Sorry I'm so dense, but after all of that, how do I access User.name?

review.created_reviews.name

Maurício Linhares wrote:

review.created_reviews.name

I wasn't sure where this should all go, but I am still getting errors. Here's what I did in script/console:

Book.find(1).reviews

=> [<#Review id: 2, reviewable_type: "Book", reviewable_id: 1, create_review_user_id: 1>]

review.created_reviews.name

=> NameError: undefined local variable or method `review' for #<Object:0x18fa38>         from (irb):9

If I put this in, I also get an error.

Book.find(1).reviews.each do |r|

?> puts review.created_reviews.name

end

NameError: undefined local variable or method `review' for #<Object:0x18fa38>         from (irb):11

Clearly I'm lost and confused. Thanks for helping out this new ruby girl.

Ok, here's what I have in my models.

class Review < ActiveRecord::Base belongs_to :reviewable, :polymorphic => true belongs_to :created_reviews, :class_name => 'User', :foreign_key => 'create_review_user_id'

This would be clearer as something singular (like 'reviewer') since a belongs_to is going to give a single associated record.

belongs_to :modified_reviews, :class_name => 'User', :foreign_key => 'modify_review_user_id' end

class User < ActiveRecord::Base has_many :reviews, :as => :reviewable has_many :created_reviews, :foreign_key => 'create_review_user_id' has_many :modified_reviews, :foreign_key => 'create_review_user_id'

Perhaps you meant for the second one to be 'modify_review_user_id'?

Sorry I'm so dense, but after all of that, how do I access User.name?

Maurício Linhares wrote:

review.created_reviews.name

I wasn't sure where this should all go, but I am still getting errors. Here's what I did in script/console:

Book.find(1).reviews

=> [<#Review id: 2, reviewable_type: "Book", reviewable_id: 1, create_review_user_id: 1>]

review.created_reviews.name

=> NameError: undefined local variable or method `review' for #<Object:0x18fa38>        from (irb):9

review = Book.find(1).reviews.first

If I put this in, I also get an error.

Book.find(1).reviews.each do |r|

?> puts review.created_reviews.name

end

NameError: undefined local variable or method `review' for #<Object:0x18fa38>        from (irb):11

Close, try: Book.find(1).reviews.each do |review| then the block will have a variable named review (rather than the r in your first attempt)

Clearly I'm lost and confused. Thanks for helping out this new ruby girl.

Be aware that your confusion seems to be a mix of Rails (specifically, ActiveRecord) conventions and Ruby syntax.

-Rob

Rob Biedenharn http://agileconsultingllc.com Rob@AgileConsultingLLC.com

Rob Biedenharn wrote:

Be aware that your confusion seems to be a mix of Rails (specifically, ActiveRecord) conventions and Ruby syntax.

Yes, I'm still new to this. Thank you so much for your patience and great help.

I've got it all working now!