I'm having a problems with :group and :uniq tags as an option to has_many through collection definition. I have many triple model associations defined on my system and I want to list elements grouped to avoid (the actual) repetition. My main model looks like that:
class Trip < ActiveRecord::Base
belongs_to :agent
has_many :trips_destinations, :class_name => "TripsDestination"
has_many :destinations, :through => :trips_destinations
has_and_belongs_to_many :vibes, join_table: :trips_vibes
has_and_belongs_to_many :verbs, join_table: :trips_verbs
has_many :trips_destinations_activities, :class_name => "TripsDestinationsActivity"
has_many :activities, :through => :trips_destinations_activities, :uniq => true
has_many :trips_destinations_hotels, :class_name => "TripsDestinationsHotel"
has_many :hotels, :through => :trips_destinations_hotels
has_many :trips_destinations_recommended_places, :class_name => "TripsDestinationsRecommendedPlace"
has_many :recommended_places, :through => :trips_destinations_recommended_places
has_many :trips_destinations_transportations, :class_name => "TripsDestinationsTransportation"
has_many :transportations, :through => :trips_destinations_transportations
...
...
end
Anyone know how to list they why avoiding repetition given the pair [trip_id, destination_id]?
And why they are repeated?
My rails version is 4 and I the image below shows the error message when passing grouping options to has_many relation.
Please help!
Your syntax for uniq is not correct for Rails 4.
has_many :activities, -> { uniq }, :through => :trips_destinations_activities
Unique now has scope syntax.
Related
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
In my rails app a user can create courses and schools (group). Each time a user creates a course or school their user_id is stored in the database table for the course or school, so a user has_many :schools and :courses and the school or course belongs_to :user. Also, the user can can attend courses and join schools (as student or professor) with a has_many through relationship model (schoolgroups for schools which has a :user_id, school_id, and :role [string], and for courses student_users (and professor_users) which has a :user_id, course_id, and :role [string]. My question is, in the user model can I just specify once that:
has_many :schools, :dependent => :destroy
has_many :courses, :dependent => :destroy
has_many :schoolgroups, :dependent => :destroy
has_many :student_users, :dependent => :destroy
has_many :professor_users, :dependent => :destroy
or would I have to have a user model that looked like this:
has_many :schools, :dependent => :destroy
has_many :courses, :dependent => :destroy
has_many :schoolgroups, :dependent => :destroy
has_many :schools, :through => :schoolgroups
has_many :student_users, :dependent => :destroy
has_many :courses, :through => :student_users
has_many :professor_users, :dependent => :destroy
has_many :courses, :through => :professor_users
You'll need to think about the ownership of the models in a bit more detail. has_many means a many-to-one relationship, so saying Professor has_many Courses means that the professor owns the courses and the course only has one professor (or does it?). So you'll do something like this:
has_many :school, :dependent => :destroy
has_many :courses, :dependent => :destroy
has_many :schoolgroups, :dependent => :destroy
On the other hand, you'll have things that the user is associated to, but other users may also be associated in the same way. For example, a user can be a student in a course, but so can many other students. For this you'll want to use has_and_belongs_to_many or HABTM which represents a many-to-many relationship:
has_and_belongs_to_many :courses_as_student, :class_name => "Course",
:join_table => "student_users"
Then in the Course class:
belongs_to :user
# or even better:
# belongs_to :professor, :class_name => "User", :foreign_key => "professor_id"
has_and_belongs_to_many :students, :class_name => "User",
:join_table => "student_users"
You can read all the details in the Rails documentation.
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 !
I have such angry associations: financings >- events >- subprograms >- programs. I want to get acces to last_financings from programs through all of them so code is:
class Fcp < Program
has_many :fcp_subprograms,
:foreign_key => 'parent_id'
has_many :subprogram_last_actual_financings,
:through => :fcp_subprograms,
:source => :last_actual_financings
class FcpSubprogram < Program
belongs_to :fcp,
:class_name => 'Fcp',
:foreign_key => 'parent_id'
has_many :events,
:foreign_key => 'fcp_id'
has_many :last_actual_financings,
:through => :events,
:source => :last_actual_financings
class Event < ActiveRecord::Base
belongs_to :fcp,
:class_name => 'Fcp',
:foreign_key => 'fcp_id'
belongs_to :fcp_subprogram,
:class_name => 'FcpSubprogram',
:foreign_key => 'fcp_id'
has_many :last_actual_financings,
:class_name => 'ActualFinancing',
:order => 'date DESC',
:limit => 1
So when I want to access to subprogram_last_actual_financings in after_initialize function I get this error
Invalid source reflection macro :has_many :through for has_many :subprogram_last_actual_financings, :through => :fcp_subprograms. Use :source to specify the source reflection.
but I have :source option in my associations. What am I doing wrong?
The error you get is about source_reflection is an invalid association, because source for has_many through must be belongs_to, has_one or has_many without through option. So you can't use :last_actual_financings as a source.
As far as I remember you can't make association with double (or more) :through. The only thing you can do is write your own sql queries.
Here is my example how to do it:
class Person
...
has_many :teams, :finder_sql =>
'SELECT DISTINCT teams.* FROM teams
INNER JOIN team_roles ON teams.id = team_roles.team_id
INNER JOIN team_members ON team_roles.id = team_members.role_id
WHERE ((team_members.person_id = #{id}))'
# other standard associations
has_many :team_members
has_many :team_roles,
:through => :team_members
# and I couldn't do:
# has_many :teams, :through => :team_roles
This is for relation Person -> has_many -> team_members -> has_many -> team_roles -> has_one - team.
Hope it helps.
This seems like a really awkward way of doing this... I'd rather make a virtual accessor in Program that calls something like this:
self.subprograms.first.events.first.financings.first(:order => 'date DESC')
consider this code
class User < ActiveRecord::Base
has_many :views
has_many :posts, :through => :views, :uniq => true
has_many :favorites
has_many :posts, :through => :favorites, :uniq => true
has_many :votes
has_many :posts, :through => :votes, :uniq => true
end
# controller code
user = User.find(3)
posts = user.posts # ??
that said i have established three relationships between posts and users, through different way. But what about the last line??? how can I tell rails that I want to get the posts through views or favorites.
You can give each association a different name, but point it at the same model using the :class_name option. Like so:
class User < ActiveRecord::Base
has_many :views
has_many :view_posts, :through => :views, :class_name => 'Post', :uniq => true,
has_many :favorites
has_many :favorite_posts, :through => :favorites, :class_name => 'Post', :uniq => true
has_many :votes
has_many :vote_posts, :through => :votes, :class_name => 'Post', :uniq => true
end
# Then...
User.find(3).favorite_posts
You may also find named_scope useful.
You have to give the associations different names. The 2nd and 3rd has_many :posts just overwrite the previous ones. You will need something like has_many :view_posts, has_many :favorite_posts, etc.