I have 3 Models:
Article (:title)
has_many :units
Dealer (:name, :adress)
has_many :units
Unit (:price, :dealer_id, :article_id)
belongs_to :article
belongs_to :dealer
I'm not sure if I'm perfectly right with my tables and I'm not sure if I should use has_many :through or just has:many and belongs to? What is exactly the difference?
And how exactly would the rails queries look like?
Article.find(:name => "Cheese").units
Article.find(:name => "cheese").units.minimum('price').dealer
Would such complex rails queries work in this kind of relationsship?
You could declare the has_many through like this:
Unit (:price, :dealer_id, :article_id)
belongs_to :article
belongs_to :dealer
Article (:title)
has_many :units
has_many :dealers, through: :units
Dealer (:name, :adress)
has_many :units
has_many :articles, through: :units
Using the has_many :objects, through: :relation let you access to the objects of a particular entry:
#dealer.articles
# and
#article.dealers
This has_many through: is a different way to do a has_and_belongs_to_many: it allows additional attributes and validations on the join model (Ruby style Guide)
Doing the following will generate 2 queries to the DB:
Article.find(:name => "Cheese").units
We know that the Unit model has an attribute article_id. The best in this case would be (assuming you know the article's id):
Unit.where(article_id: article.id)
This generate only one query to the DB.
Related
I'm trying to set up some new classes and associations in a Rails 2.2.2 app (please don't ask me to upgrade it).
The idea is that there is a Conversation class. It can have many participants, which are a mix of User and Pupil records, and it can have many owners, which can be a mix of a variety of things, including User and Pupil records. This is what I have so far:
#the :owner association can point to *anything*, including a Pupil or User record
class ConversationOwnership < ActiveRecord::Base
belongs_to :conversation
belongs_to :owner, :polymorphic => true
end
#the :participant association will point to either a Pupil or User record
class ConversationParticipant < ActiveRecord::Base
belongs_to :conversation
belongs_to :participant, :polymorphic => true
end
class Conversation < ActiveRecord::Base
has_many :conversation_ownerships
has_many :owners, :through => :conversation_ownerships
has_many :conversation_participants
has_many :participants, :through => :conversation_participants
end
This currently isn't working:
Conversation.first.participants
=> ActiveRecord::HasManyThroughAssociationPolymorphicError: Cannot have a has_many :through association 'Conversation#participants' on the polymorphic object 'Participant#participant'.
Now, I know that the has_many_polymorphs plugin was designed to solve this very problem, but the problem with using that is that it automatically makes associations for each of the listed classes, and because User and Pupil are in both participants and owners, they would clash:
OWNER_CLASSES = [:ilps, :lessons, :digilearning_modules, :resources, :pupil_groups, :pupils, :users]
PARTICIPANT_CLASSES = [:pupils, :users, :contacts, :parent_carers]
has_many_polymorphs :participants, :from => PARTICIPANT_CLASSES, :through => :conversation_participants, :order => "conversation_participants.created_at"
has_many_polymorphs :owners, :from => OWNER_CLASSES, :through => :conversation_ownerships, :order => "conversation_ownerships.owner_type, conversation_ownerships.created_at"
With this, the first hmp makes a .pupils association which effectively means "participants that are Pupils", and the second hmp also makes a .pupils association which effectively means "owners that are Pupils".
I don't want these extra associations that has_many_polymorphs brings, which is why i thought I would roll my own associations. But, I can't get past that Cannot have a has_many :through association 'Conversation#participants' on the polymorphic object 'Participant#participant'. error.
This feels like it should be possible - is it?
I have a model called House and I want to be able to associate houses with each other to show recommendations.
So I would expect that given a house, I should be able to ask: house.recommended_houses. A house could be recommended for more than one house.
I was thinking on having a table that would store this association (I don't know the name yet), so it would have the following columns:
recommended_house_id
recommended_for_house_id
I am failing to understand how would I hook this up with my House model. What would the associations look like, and also what name should I be using for that join model?
This should get you started:
class House < ApplicationRecord
has_and_belongs_to_many :recommendations,
class_name: "House",
foreign_key: "recommended_by_id",
association_foreign_key: "recommendation_id"
end
What you're describing is called a self-referential association.
You can set up a join table (recommendations) and the associated model:
class Recommendation < ActiveRecord::Base
belongs_to :house
belongs_to :recommended_house, :class_name => 'House'
end
and then use has_many, :through relationships within the House model to set up the relationships you're looking for.
class House < ActiveRecord::Base
has_many :recommendations
has_many :recommended_houses, :through => :recommendations
has_many :inverse_recommendations, :class_name => "Recommendation", :foreign_key => "recommended_house_id"
has_many :recommended_by_houses, :through => :inverse_recommendations, :source => :house
end
Now you can use both house.recommended_houses and house.recommended_by_houses.
I have two models Type and Activity. Type has_many activities and Activity has_many types. To do this I used the has_many :through thing. This is how it looks like
Activity
has_many :typeitems
has_many :types, :through => :typeitem
Typeitem
belongs_to :activity
belongs_to :type
Type
has_many :typeitems
belongs_to :activity
This does not feel right though. I would want to query 2 things
Activities of a particular type
Types of a particular activity
When I went into rails console and typed types.activity I got a nil which means I will get a single object. Should I change the belongs_to in Type model to has_many.But then it's back to many-to-many. There should be a way.
I looked at the docs and found has_and_belongs_to_many. I also read this
You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.
I am not using it now but I might want to in the future.
Both sides need a has_many :through:
Activity
has_many :typeitems
has_many :types, :through => :typeitem
Typeitem
belongs_to :activity
belongs_to :type
Type
has_many :typeitems
has_many :activities, through: :typeitems
I am trying to design the relationships for a doctor and their training programs/specialties. Example below:
A program has one specialty/program (ex. University Neurology Training program specializes in neurology)
A doctor can have multiple programs and thus multiple specialties (ex. Dr. Smith is a neurologist who attended University Neurology Training program, while Dr. Jones is a neurologist and pediatrician who attended University Neurology Traning Program and Big Hospital Pediatrics Program)
It would seem that it could be set up as a has_many :through ... However, this doesn't seem efficient or correct when I try to conceptualize it. I have another largely unrelated model that ties in with the specialty (but not the program), that's why I don't combine the program and specialty. I should be able to access User.programs.all and Program.users.all :
Model User:
has_many programs
has_many specialties, :through => :programs
Model Program:
belongs_to :user
belongs_to :specialty
Model Specialty:
has_many :users, :through => :program
has_many :programs
You can have models something like below.
class Doctor
has_many :specialties, :through => :practices
has_many : enrollments
has_many :programs , :through => : enrollments
end
class Program
has_many : enrollments
has_many :doctors, :through => : enrollments
belongs_to :specialty
end
class Enrollment
belongs_to : doctor
belongs_to :program
end
class Specialty
has_many :practices
has_many :doctors, :through => :practices
has_many :programs
end
class Practice
belongs_to :doctor
belongs_to :specialty
end
Hope it helps.
Update
If a doctor can only have specialty via a program then it can be modeled like this.
class Doctor
has_many :enrollments
has_many :programs, :through => :enrollments
end
class Program
has_many :enrollments
has_many :doctors, :through => :enrollments
belongs_to :specialty
end
class Enrollment
belongs_to :doctor
belongs_to :program
end
class Specialty
has_many :programs
has_many :enrollments , :through => :programs
end
To get all the doctors of a specialty eg neurology.
#neurology.enrollments.collect { |c| c.doctor }.uniq
Or
Doctor.includes(:enrollments).where(:enrollments => {:specialty_id => #neurology.id})
To get all specialties of a doctor you have to do like this.
#doctor.programs.collect { |p| p.specialty }.uniq
Do not link to specialty via programs.
Make it so that specialties and programs are independent.
It seems like you have a good chance of running into a case where doctor has a specialty, but did not attend any meaningful "programs".
You may add as "soft" link from specialties to programs: in model Specialty add "belongs_to :program", with a possibility of program being NULL.
I'm having trouble figuring out the best way to do this. I have a User model and a Tournament model and I set up a has_many :through relation between these two models called 'followed_tournaments' so that users can follow a tournament. As such, I already have a has_many :tournaments in the User model and a has_many :users in the Tournament model so that a tournament has many followers and a user can follow many tournaments.
I'd like to set up another habtm or has_many :through relationship so that a User can be considered a "contributor" to a Tournament -- a completely separate relationship than what I already set up. I'd like a tournament to have any number of contributors and the user to contribute to many tournaments.
What's the best way to go about implementing this?
Use source or class_name
class Tournament < ActiveRecord::Base
has_many :users # ... whatever
has_many :contributions
# using class_name
has_many :contributors, :through => :contributions
# using source
has_many :contributors, :through => :contributions, :source => :user
end
class Contribution < ActiveRecord::Base
belongs_to :tournament
# using class_name
belongs_to :contributor, :class_name => 'User'
# using source
belongs_to :user
end