Polymorphic many-to-many - ruby-on-rails

The following examples differ only in the Book and Movie models.
Example 1: Book has_many :taggings, :as => :taggable
Example 2: Book has_many :taggings, :through => :taggable
What does this difference mean?
Example 1:
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
class Tag < ActiveRecord::Base
has_many :taggings
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
Example 2:
class Book < ActiveRecord::Base
has_many :taggings, :through => :taggable
has_many :tags, :through => :taggings
end
class Movie < ActiveRecord::Base
has_many :taggings, :through => :taggable
has_many :tags, :through => :taggings
end
class Tag < ActiveRecord::Base
has_many :taggings
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

Are you sure the 2nd example works? I don't have an rails environment in windows.
For my understanding about your question,
case1.
has_many :taggings, :as => :taggable
here the "taggable" is not an exact Model name, it's just an alias. (there's no Taggable class)
case2.
has_many :taggings, :through => :taggable
here the "taggable" must be an real (exsting) model, I mean there must be an Taggable class somewhere.
refer: http://guides.rubyonrails.org/association_basics.html#polymorphic-associations

Assuming the examples worked .. and taggable was a real model .. which as Siwei suggests probably doesn't actually work ...
:as defines a polymorphic association
and
:through says that
if A has many B's
and B has many C's
then A has many C's through B

Related

uninitialized constant ruby model

I have 3 classes:
1. Article
class Article < ActiveRecord::Base
has_many :categories_articles
has_many :subcategories_articles
has_many :categories, :through => :categories_articles
has_many :subcategories, :through => :subcategories_articles
end
2.Category
class Category < ActiveRecord::Base
has_many :articles
has_many :categories_articles
has_many :categories_subcategories
has_many :subcategories, :through => :categories_subcategories
has_many :articles, :through => :categories_articles
end
3.The third class is the union of the first two, category_article
class CategoryArticle < ActiveRecord::Base
belongs_to :category
belongs_to :article
end
so, when i called in the view
<% f.collection_select(:category_ids, Category.all, :id, :name, {include_blank:"selects"},{class:'form-control select2 multi', :required=>true, multiple: true}) %>
I get this error:
uninitialized constant Article::CategoriesArticle
The same goes for the class Subcategory and subcategory_article
Try
has_many :category_articles
And
has_many :subcategory_articles
You'll also have to change these:
has_many :categories, :through => :categories_articles
has_many :subcategories, :through => :subcategories_articles
To something like:
has_many :categories, :through => :category_articles
has_many :subcategories, :through => :subcategory_articles
Rails doesn't pluralize both components of the composite table names. Just the last component.

Rails - has many polymorphic tagging not working correctly

I have two tables for tagging so that I can attach tags to any models, it works likes so…
There's a tagged item join table which has a tag_id column and then two other columns for polymorphism: taggable_type and taggable_id…
class TaggedItem < ActiveRecord::Base
attr_accessible :taggable_id, :taggable_type, :tag_id
belongs_to :taggable, :polymorphic => true
belongs_to :tag
end
There's also all of the things that can have tags, for example here's a product and image model with tags attached:
class Product < ActiveRecord::Base
has_many :tagged_items, :as => :taggable, :dependent => :destroy
has_many :tags, :through => :tagged_items
end
class Image < ActiveRecord::Base
has_many :tagged_items, :as => :taggable, :dependent => :destroy
has_many :tags, :through => :tagged_items
end
The problem is with the tag model, I can seem to get the reverse work, on the tag modle I want to have a has_many images and has_many products like so:
class Tag < ActiveRecord::Base
has_many :tagged_items, :dependent => :destroy
has_many :products, :through => :tagged_items
has_many :images, :through => :tagged_items
end
This is causing an error, I was wondering how I can fix this. So the tag table works through the polymorphic tagged items table.
Any help would be much appreciated. Thanks!
Edit:
Could not find the source association(s) :product or :products in model TaggedItem. Try 'has_many :products, :through => :tagged_items, :source => <name>'. Is it one of :taggable or :tag?
The has_many :through associations in your Tag model are not able to get the source association for Product and Image from the TaggedItem Model. e.g. has_many :products, :through => :tagged_items will look for a direct association belongs_to :product in TaggedItem which in case of polymorphic association is written as belongs_to :taggable, :polymorphic => true. So for the Tag model to understand exact source of the association we need to add an option :source and its type as :source_type
So change your Tag model associations to look like
class Tag < ActiveRecord::Base
has_many :tagged_items, :dependent => :destroy
has_many :products, :through => :tagged_items, :source => :taggable, :source_type => 'Product'
has_many :images, :through => :tagged_items, :source => :taggable, :source_type => 'Image'
end
This should fix your problem. :)
you do not need the as option when you set up the Tag association to TaggedItem. :as => :taggable would mean that tag on tagged item is polymorphic which it is not. Instead the other side is, ie., the items that are taggable as your name cleverly suggests :).
class Tag < ActiveRecord::Base
has_many :tagged_items, :dependent => :destroy
has_many :products, :through => :tagged_items
has_many :images, :through => :tagged_items
end

Rails Relationship Help

I have simplified my design to make this question more clear. (Design of models below)
What I am trying to do is from a CourseEnrollment get all the PatientCourseSteps for that enrollment only (An enrollment consists of a patient and a course).
Here is what I have tried:
#Gives me ALL the patient course steps regardless of the course
course_enrollment.patient.patient_course_steps
#Gives me ALL the patient course steps regardless of the patient
course_enrollment.course.patient_course_steps
Below is the models that are necessary
class CourseEnrollment < ActiveRecord::Base
belongs_to :patient
belongs_to :course
end
class Course < ActiveRecord::Base
has_many :course_steps, :dependent => :destroy
has_many :steps, :through => :course_steps
has_many :course_enrollments, :dependent => :destroy
has_many :patients, :through =>:course_enrollments
has_many :patient_course_steps, :dependent => :destroy
end
class Patient < ActiveRecord::Base
belongs_to :user, :dependent => :destroy
has_many :enrollments, :dependent => :destroy
has_many :clients, :through => :enrollments
has_many :course_enrollments, :dependent => :destroy
has_many :courses, :through => :course_enrollments
has_many :patient_course_steps, :dependent => :destroy
end
class Step < ActiveRecord::Base
belongs_to :step_type
belongs_to :client
has_one :step_quiz, :dependent => :destroy
has_one :step_survey, :dependent => :destroy
has_one :step_text, :dependent => :destroy
has_one :step_download, :dependent => :destroy
has_one :step_video, :dependent => :destroy
has_one :step_presentation, :dependent => :destroy
has_many :course_steps, :dependent => :destroy
has_many :courses, :through => :course_steps
has_many :patient_course_steps, :dependent => :destroy
end
class PatientCourseStep < ActiveRecord::Base
belongs_to :patient
belongs_to :course
belongs_to :step
end
What I ended up doing was adding a method to CourseEnrollment named patient_course_steps which queries for what I need.
def patient_course_steps
PatientCourseStep.where(:patient_id => self.patient_id, :course_id => self.course_id)
end

:has_many relationship with :through on another relationship with :through

My setup is as follows:
class User < ActiveRecord::Base
has_many :owners, :dependent => :destroy
has_many :properties, :through => :owners
end
class Owner < ActiveRecord::Base
belongs_to :user
belongs_to :property
end
class Property < ActiveRecord::Base
has_many :owners, :dependent => :destroy
has_many :users, :through => :owners
has_many :datafiles, :dependent => :destroy
end
class Datafile < ActiveRecord::Base
belongs_to :property
end
Now I'd like to be able to do #user.datafiles.
I tried has_many :datafiles, :through => :properties, :source => :datafiles but there appears to be a problem with a :through on something that's already went to a :through. So how would I go about to try and manage what I'm trying to do here?
Thank you in advance.
2 approaches;
1>
class User < AR
has_many :owners, :dependent => :destroy
has_many :properties, :through => :owners
has_many datafiles
end
class Datafile < AR
belongs_to :user
belongs_to :property
end
Your requirement of user.datafiles should be fulfilled with this.
If you want a nested has_many through, you'll need to use a plugin which is the 2nd approach.
2>
You can find it here.
The plugin works out of the box and does the job.
How about something like:
#user.rb
def datafiles
Property.find(:all, :joins => :owners, :conditions => ['owners.user_id = self.id'], :include => :datafile).collect(&:datafile)

Nested Has Many Through Plugin and Named Scopes

I have a User Model(:name, :password, :email), and Event model(:name, :etc) and Interest model (:name) [>all singular<]
Then I created two join tables -> UsersInterests and EventsInterests; each not containing a primary key and only comprised of the user_id/interest_id and event_id/interest_id respectively. [>plural<]
My Models Use the Nested Has Many Through Plugin
user.rb => has_many :users_interests
has_many :interests, :through => :users_interests
has_many :events_interests, :through => :interests
has_many :events, :through => :events_interests
event.rb => has_many :events_interests
has_many :interests, :through => :events_interests
has_many :users_interests, :through => :interests
has_many :users, :through => :users_interests
interest.rb => has_and_belongs_to_many :users
has_and_belongs_to_many :events
events_interests.rb => belongs_to :interests
belongs_to :events
users_interests.rb => belongs_to :users
belongs_to :interests
Whew..ok So I wanted to created a named_scope of that find all the events that share interest with a particular user. Here is some code someone helped me with.
named_scope :shares_interest_with_users, lambda {|user|
{ :joins => :users_interests,
:conditions => {:users_interests => {:user_id => user}}
}}
When i run from the controller =>
#user = User.find(1)
#events = Event.shares_interest_with_user(#user)
I get the error :
uninitialized constant Event::EventsInterest
Can anyone see what i messed up?
You must have named something wrong along the way. At a glance I'd say you have a file or class named incorrectly. Remember model names MUST always be singular, both in file and class names or else Rails won't make the connection. Another source of your problem is that arguments to belongs_to must also be singular. Even if you had got things right, the HABTM relationship in interests with users would have thrown an error when you ran the named scope.
I was able to solve your error with the following models.
user.rb
class User < ActiveRecord::Base
has_many :users_interests
has_many :interests, :through => :users_interests
has_many :events_interests, :through => :interests
has_many :events, :through => :events_interests
end
users_interest.rb
class UsersInterest < ActiveRecord::Base
belongs_to :user
belongs_to :interest
end
interest.rb
class Interest < ActiveRecord::Base
has_many :users,:through => :users_interests
has_many :users_interests
has_many :events_interests
has_many :events, :through => :events_interests
end
**events_interest.rb
class EventsInterest <ActiveRecord::Base
belongs_to :interest
belongs_to :event
end
event.rb
class Event <ActiveRecord::Base
has_many :events_interests
has_many :interests, :through => :events_interests
has_many :users_interests, :through => :interests
has_many :users, :through => :users_interests
named_scope :shares_interest_with_users, lambda {|user|
{ :joins => :users_interests,
:conditions => {:users_interests => {:user_id => user}}
}
}
end

Resources