has_many / belongs_to associations in RoR - ruby-on-rails

I've read the guide on associations but I feel like I'm still not completely comprehending so I want to ask a couple of questions just to be sure. Let's say I am making an app that will, among other things, list large cities all over the world. I would plan on having a view that starts at continent level and can be filtered down. So I would start with a Continent model. And then a Country model. Now, within the Continent model I would define an association as has_many :countries. And in the Country model I would use belongs_to :continents. That much I grasp. So my next model would be a model for states / province. Let's just call it Province since that is more common throughout the world. So now I have my Province model, and I would use belongs_to :country. And likewise Countries would have has_many :provinces. My first question is, how do I describe the association between Province and Continent? Has_many through describes associations where both models have many. A Province only has one Continent. Has_one through describes a relationship between objects that have a one to one relationship via a third object. Again, this isn't the case because a Continent will have many Provinces. So that is my primary question.. how to describe relationships that exist in a one to many through context. My second question would be just asking for tips on writing the migrations for this in a situation where I add another layer, say County, later on. But the main problem is just understand how to express the relationships I described. Or if they even need to be expressed.
ETA: If I were to use the has_many_through association, do I necessarily need to create a join table ( continent_province ), or can I simply use the countries table ie has_many :provinces -> through :countries?

Don't get too wound up around a couple of small examples in some doc somewhere. The relationship support is wonderfully flexible. In the end, just give it a try -- I have a Tester application that has all sorts of proof of concepts in it -- that's its purpose.
class Project
# one-to-many
has_many :scenarios
# linking through scenarios
has_many :unittests, :through => :scenarios
# polymorphic relationship, everything can be relation to one or more appls
has_many :appllinks, :as => :applinkable, :dependent => :destroy
has_many :appls, :through => :appllinks, :order => 'name'
blah blah blah
end
class Scenario
# many-to-one
belongs_to :project
# many-to-many
has_many :scenariotests
has_many :unittests, :through => :scenariotests
# polymorphic relationship, everything can be relation to one or more appls
has_many :appllinks, :as => :applinkable, :dependent => :destroy
has_many :appls, :through => :appllinks, :order => 'name'
blah blah blah
end
class Unittest
# many-to-many
has_many :scenariotests
has_many :scenarios, :through => :scenariotests
# polymorphic relationship, everything can be relation to one or more appls
has_many :appllinks, :as => :applinkable, :dependent => :destroy
has_many :appls, :through => :appllinks, :order => 'name'
blah blah blah
end

Related

Related model can be one of two other models

I currently have essentially 3 models at the moment. I have Project, User and Contact.
I'm trying to assign users or contacts as a sort of 'member' to each project. I initially thought that a linking table here would suffice, for example ProjectMembers but i'm currently hitting a brick wall in my thought process when it comes to a project member only being allowed to be either a Contact or a User and whether to handle this via a relationship or by code in the model or the controller by checking which between user_id or contact_id was not null.
I had a look at polymorphic associations which looked promising, but somehow I ended up with the association backwards (Projects were being entered into the ProjectMember table as the type, rather than User or Contact) and confused myself even more.
The final output I would pretty much like would be simply to have the ability to run something like Project.first.project_members and have those members return with the role in that project. It'd be even nicer if I could run User.first.projects/Contact.first.projects and get those too, but that's something I can figure out down the line.
Any pointers would be greatly appreciated.
Polymorphic association is something that you should go for here.
First: Project has a many to many relationship with User and Contact, hence you need a join table.
Second: Since you need to call something like Project.first.project_members, you should have a polymorphic association.
For this scenario, you should create a polymorphic join table.
Let's say you have the polymorphic table ProjectMember with memberable_id, memberable_type and project_id (well I have a hang of railcasts).
class ProjectMember < ActiveRecord::Base
belongs_to :memberable, :polymorphic => true
belongs_to :project
end
and then in your User model,
class User < ActiveRecord::Base
has_many :project_members, :as => :memberable
has_many :projects, through: :project_members
end
and in Contact model
class Member < ActiveRecord::Base
has_many :project_members, :as => :memberable
has_many :projects, through: :project_members
end
and in your Project model
class Project < ActiveRecord::Base
has_many :users, :through => :project_members, :source => :memberable, :source_type => 'User'
has_many :contacts, :through => :project_members, :source => :memberable, :source_type => 'Contact'
has_many :project_members
end
Now you can call your desired associations, Project.first.project_members, User.first.projects or Contact.first.projects. Hope this helps.
Thanks

ActiveRecord has_many :through association through multiple sources

I have a self referencing has_many :through model that has another has_and_belongs_to_many with another model. Basically something like this:
class Foo << ActiveRecord::Base
has_and_belongs_to_many :bars
has_many :foo_links
has_many :foo_parents, :through => :foo_links, :foreign_key => :foo_parent_id, :class_name => "Foo"
has_many :foo_children, :through => :foo_links, :foreign_key => :foo_child_id, :class_name => "Foo"
end
I'd like to be able to have a foo_child item be able to belong to any bars to which it is assigned, as well as any bars to which one of its foo_ancestors (foo_parents and their foo_parents, etc) is assigned. I was basically hoping to put together something like this:
has_many :inherited_bars, :through => :foo_parents, :source => [:bars, :inherited_bars]
I've never seen such an example, but I was wondering if it is possible to have an association that is a merger of associations from a through association.
I think has_many association always tied to have an id somewhere to indicate the relationship, and allows you to modify this. Eg. you can add a new element to a has_many array, and the result is persisted back to the database. If you can merge two sources together, you lose the ability to link the rows by this.
A possible approach is this, readonly way:
has_many :a
has_many :b
def sum
a + b
end

Multiple levels of have_many in a model

I am working with Ruby on Rails (specifically the ActiveRecord) and I am trying to decide whether or not it is a good idea to link my models using multiple levels.
class Student < ActiveRecord::Base
has_many :student_sections
has_many :sections, :through => :student_sections
has_many :courses, :through => :sections
end
It seems like this would work, but I don't have a lot of experience in ActiveRecord. Is there any reason not to do this?
This is fine but you should bear in mind that the courses association is effectively only a 'get' association (as opposed to 'get and set'). What i mean by that is that you can say
#student.courses
(after doing neo's fix) to get a list of courses, but you can't do
#student.courses << #course
as rails doesn't have the section info required to make the necessary joins between the student and the course.
you need to add :source attribute
has_many :sections, :through => :student_sections, :source => 'your_source'

Setting a type for one side of a many-to-many association with a join model

I set up a public github app (see: https://github.com/greenplastik/testapp to download) to work through a problem I'm having with specifying a type on one side of a many-to-many association between two models, via a join model.
Given Person and Book models and a Book_Person join model, I want to be able to do the following:
#book = Book.first
#book.people # lists people for book
#book.authors # lists author-type people for book
#book.editors # lists editor-type people for book
and
#person = Person.first
#person.books # lists books for people
This app was set up in part using the instructions found through Google. There's a link to those instructions in the README of my testapp.
I tried, as best I could, to remove the inconsistencies and typos. I can't get it to work.
Any help would be appreciated. I've included the sqlite database for easier testing on your end.
I'm sorry i haven't got time to post neither a full solution or a tested one, but this should at least put you on the right track..
The best way to achieve what you're looking for is with Single Table Inheritance used with a polymorphic relationship.
I'd suggest redefining Author and Editor to be subclasses of Person
As in
class Person < ActiveRecord::Base
has_many :book_people
has_many :books, :through => :book_people
end
class Author < Person
end
class Editor < Person
end
class BookPerson < ActiveRecord::Base
belongs_to :person, :polymorphic => true
belongs_to :book
end
class Book < ActiveRecord::Base
has_many :book_people
has_many :people, :through => :book_people
has_many :authors, :through => :book_people, :source => :person, :source_type => "Author"
has_many :editors, :through => :book_people, :source => :person, :source_type => "Editor"
end
However this would be suboptimal if a single person could fill all three roles. There's probably a way around that by using the same STI name for the three of them. But then you'd make it harder to query for all authors.

How to do multiple many to many relationships between the same two tables

I have a model of a club where I want to model the two entities Meeting and Member.
There are actually two many-to-many relationships between these entities though, as for any meeting a Member can either be a Speaker or a Guest. Now I am an OO thinker, so would normally just create the two classes and each one would just have two arrays of the other inside, but rails is making me think a bit more data centric here, so I realise I need to break these M2M relationships up with join tables Speakers and Guests which I have done, but now I am having trouble describing the relationships in the models.
The two join table models both have "belongs_to :meeting" and "belongs_to :member" and I think that should be sufficient.
I am not however sure about the Meeting and Member models though.
Each one has "has_many :guests" and "has_many: speakers" but I am not sure if I also want to go:
has_many :members, :through => :guests
has_many :members, :through => :speakers
But I suspect that this is like declaring two "members" that will clash.
I also thought about:
has_many :guests, :through => :guests
has_many :speakers, :through => :speakers
Does that make sense? How would ActiveRecord know that they are in fact Members?
I have found heaps of examples of polymorphic m2m relationships and m2m relationships where 1 table references itself, but no good examples to help me mode this situation where two separate tables have two different m2m relationships.
Anyone got any tips?
You need to pick different association names and then specify the model:
class Meeting
has_many :guests
has_many :speakers
has_many :guest_members, :through => :guests, :source => 'Member'
has_many :speaker_members, :through => :speakers, :source => 'Member'
end

Resources