Rails, has_many polymorphic association to one model - ruby-on-rails

I would like to have one model (event) that has multiple polymorphic HABTM associations to the same user model.
It should work something like this:
Event < ActiveRecord::Base
has_many :admin_users, :through => :event_users, :source => :userable, :source_type => 'User'
has_many :participating_users, :through => :event_users, :source => :userable, :source_type => 'User'
has_many :paid_users, :through => :event_users, :source => :userable, :source_type => 'User'
has_many :done_users, :through => :event_users, :source => :userable, :source_type => 'User'end
class EventUser < ActiveRecord::Base
belongs_to :userable, :polymorphic => true
belongs_to :event
end
User < ActiveRecord::Bas
has_many :event_users, :as => :userable
has_many :events, :through => :event_users
end
This almost works!! The problem is though that userable_type gets the type "User" for all diffrent associations. Is it possible to solve this?

I'm afraid your associations look totally wrong. First of all, if you have a 'has_many' on one side of a association, you have to have a 'belongs_to' on the other side. Secondly, I'm guessing done_user, admin_user etc inherit from User. Am i correct ?
And how different are participating_user, admin_user etc different from each other? Do you really need classes for each of these, can you make do with named scopes?
I would suggest you simplify your data model.
Right now your modeling looks fuzzy. Please do elaborate.
EDIT
Honestly, I think your modeling is over complicated. If I were you, given your descriptions of *_users, I would just have named scopes to retrieve that type of an user. So in effect
class Event < ActiveRecord::Base
has_many :event_users
has_many :users, :through => :event_users
end
class User < ActiveRecord::Base
has_many :event_users
has_many :events, :through => :event_users
end
So now, write named scopes in your User model to retrieve *_user.
Here's an excellent screencast on named_scopes.

Related

Correct association for Twitter-style posts by followed users in rails

I'm trying to list all the posts made by a user's followed users, and not sure how to correctly set up the associations. This has been discussed in various forms but I can't find a definitive answer that works in this case. My current associations are as follows:
user.rb:
has_many :followers, :class_name => 'UserFollower', :foreign_key => 'user_id'
has_many :following, :class_name => 'UserFollower', :foreign_key => 'follower_id'
has_many :posts
user_follower.rb:
belongs_to :user
belongs_to :follower, :class_name => 'User'
post.rb
belongs_to :user
I want to be able to do something like current_user.following_user_posts. So I'm trying to define an association in my user.rb something like:
has_many :following_user_posts, :through => :following
Obviously this isn't quite right, but where am I going wrong?
Michael hartle answers this exactly in his tutorial using his Micropost.from_users_followed_by method http://ruby.railstutorial.org/chapters/following-users#sec-a_first_feed_implementation
Figured this out, seems like a simple problem but the terminology makes it quite confusing. I have the following in by user.rb:
has_many :user_followers
has_many :followers, :through => :user_followers, :source => :follower #the users that are following this user
has_many :user_followings, :class_name => 'UserFollower', :foreign_key => "follower_id"
has_many :following, :through => :user_followings, :source => :user #the users that this user is following
has_many :following_user_posts, :through => :user_followings, :source => :posts
And in my user_follower.rb:
belongs_to :user
belongs_to :follower, :class_name => 'User'
has_many :posts, :through => :user, :source => :posts
The resulting query uses 2 inner joins - I have no idea how well this will scale but its working fine for me on development. So now, I can simply use current_user.following_user_posts

Rails: Association was not found; perhaps you misspelled it?

I have a User model with a has_many :through relationship to the Publication model. The Publication model in turn has a has_many :through relationship to Author:
class User < ActiveRecord::Base
has_many :library_publications, :dependent => :destroy, :class_name => "Library::Publication"
has_many :publications, :through => :library_publications
end
class Library::Publication < ActiveRecord::Base
belongs_to :publication
belongs_to :user
end
class Publication < PublicationBase
has_many :library_publications, :dependent => :destroy, :class_name => "Library::Publication"
has_many :users, :through => :library_publications
has_many :publication_contributions, :dependent => :destroy, :class_name => "Publication::Contribution"
has_many :authors, :through => :publication_contributions
end
class Author < AuthorBase
has_many :publication_contributions, :dependent => :destroy, :class_name => "Publication::Contribution"
has_many :publications, :through => :publication_contributions
end
class Publication::Contribution < Publication::ContributionBase
belongs_to :publication, :class_name => "Publication"
belongs_to :author, :class_name => "Author"
end
As far as I can tell, all the associations are written correctly. However, when I try to eagerload authors from a user:
#user.library_publications.includes(:publication => [:authors])
I get this error:
Association named 'authors' was not found; perhaps you misspelled it?
What might be the cause of this?
After experimenting a little, I discovered that all of publication's associations were broken. This led to me to looking for larger problems, and eventually I discovered that this issue was caused by one of the join-table being namespaced, Library::Publication. When I de-namespaced it, publication's associations began working again.
I'm not sure why this happened, though. If anyone has an explanation, please share.

What is the best way to handle 4 way relation between 2 models?

I have two models: Company and User
This is the situation:
Company can follow another company
User can follow a company
User can follow another user
What is the best way to define the relationships and how will the join model look like?
Also, are there any best practises when addressing such situations?
Update
Sorry, to have not mentioned this earlier. I am aware of the various relationship types available. My question is 'which is the best fit'?
Regarding your question I would suggest you to go through couple of Railscasts videos:
http://railscasts.com/episodes/47-two-many-to-many
http://railscasts.com/episodes/154-polymorphic-association
And this is described very well on RubyonRails website
http://guides.rubyonrails.org/association_basics.html
I would say look these for your case:
http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
http://guides.rubyonrails.org/association_basics.html#the-has_and_belongs_to_many-association
I hope this will help you.
Thanks to polymorphic associations, we can put all relations into one table which like this:
create_table :follows do |t|
t.references :followable, :polymorphic => true
t.references :followed_by, :polymorphic => true
end
Then the models are:
class User < ActiveRecord::Base
has_many :following_objects, :class_name => 'Follow', :as => :followed_by
has_many :followed_objects, :class_name => 'Follow', :as => :followable
end
class Company < ActiveRecord::Base
has_many :following_objects, :class_name => 'Follow', :as => :followed_by
has_many :followed_objects, :class_name => 'Follow', :as => :followable
end
class Follow < ActiveRecord::Base
belongs_to :followable, :polymorphic => true
belongs_to :followed_by, :polymorphic => true
end
Sorry for the ugly names.
A basic idea would be to use two self-referencing assocations:
User -> Friendship <- User
Company -> Partnership <- Company
models/user.rb
has_many :friendships
has_many :friends, :through => :friendships
has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id"
has_many :inverse_friends, :through => :inverse_friendships, :source => :user
models/friendship.rb
belongs_to :user
belongs_to :friend, :class_name => "User"
models/company.rb
has_many :partnerships
has_many :partners, :through => :partnerships
has_many :inverse_partnerships, :class_name => "Partnership", :foreign_key => "partner_id"
has_many :inverse_partners, :through => :inverse_partnerships, :source => :company
models/partnership.rb
belongs_to :company
belongs_to :partner, :class_name => "Company"
And one many-to-many assocation:
User -> CompanyUser <- Company
models/user.rb
has_and_belongs_to_many :companies
models/company.rb
has_and_belongs_to_many :users
So for this implementation you will need 5 tables (users, friendships, companies, partnerships and companies_users) if you are using a RDBMS.
You can get a nice example in this screencast:
http://railscasts.com/episodes/163-self-referential-association

Need help to understand :source_type option of has_one/has_many through of Rails

In Rails 3.1, the documentation says
"4.2.2.13 :source_type
The :source_type option specifies the source association type for a has_one :through association that proceeds through a polymorphic association.
"
I just read :source explanation but still dont get what source_type is used for?
:source_type deals with associations that are polymorphic. That is to say, if you have a relationship like this:
class Tag < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :books, :through => :taggings, :source => :taggable, :source_type => "Book"
has_many :movies, :through => :taggings, :source => :taggable, :source_type => "Movie"
end
class Tagging < ActiveRecord::Base
belongs_to :taggable, :polymorphic => true
belongs_to :tag
end
class Book < ActiveRecord::Base
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
end
class Movie < ActiveRecord::Base
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
end
Then the source type allows you to make queries like this:
"Find me all of the books that have been tagged with the tag named 'Fun'"
tag = tag.find_by_name('Fun')
tag.books
Without source type, you wouldn't be able to do that, you could only get a collection of objects that were tagged with 'Fun'. If you only specificed source, it wouldn't know which kind of class the objects were, so you it wouldn't know which table in the DB to pull from. The source_type Informs it of which type of object you are trying to retreive.
This is taken from this blog post: http://www.brentmc79.com/posts/polymorphic-many-to-many-associations-in-rails
Hope it helps.
☝🏼 this message is about the post above
(I don't have enough reputation yet to add this as comment...)
Thanks #TheDelChop for that simple and perfect use case. I just propose to complete your perfect explanation with this simple schema which describe your user story. Just in case of.
Thank you !

In rails 2.3, how can I retrieve a collection of objects from a second-order has_many association?

I have a Partner model that has_and_belongs_to_many Projects, while each Project has_many Sites. I want to retrieve all sites for a given partner (and am not interested in the projects in between at the moment).
I have accomplished what I need through a named_scope on the Site model, and a project.sites instance method that wraps a call to the Site named scope, as follows:
class Partner < ActiveRecord::Base
has_and_belongs_to_many :projects
def sites
Site.for_partner_id(self.id)
end
end
class Project < ActiveRecord::Base
has_many :sites
end
class Site < ActiveRecord::Base
belongs_to :project
named_scope :for_partner_id, lambda {|partner_id|
{ :include=>{:project=>:partners},
:conditions=>"partners.id = #{partner_id}"
}
}
end
Now, given a partner instance, I can call partner.sites and get back a collection of all sites associated with the partner. This is precisely the behavior I want, but I'm wondering if there's another way to do this using only activerecord associations, without the named scope?
I had a similar deep nesting query/collection problem here (I had to threaten to repeat data before anyone would answer my 4 questions, clever):
Is it appropriate to repeat data in models to satisfy using law of demeter in collections?
The trick is this gem http://rubygems.org/gems/nested_has_many_through which can do something like this:
class Author < User
has_many :posts
has_many :categories, :through => :posts, :uniq => true
has_many :similar_posts, :through => :categories, :source => :posts
has_many :similar_authors, :through => :similar_posts, :source => :author, :uniq => true
has_many :posts_of_similar_authors, :through => :similar_authors, :source => :posts, :uniq => true
has_many :commenters, :through => :posts, :uniq => true
end
class Post < ActiveRecord::Base
belongs_to :author
belongs_to :category
has_many :comments
has_many :commenters, :through => :comments, :source => :user, :uniq => true
end
This has super-simplified my queries and collections. I hope you find an answer to your problem, it's a tough one!

Resources