I have 3 models:
sophead
od
od_item
sophead has many ods and
od has many od_items.
each od_item belongs to one od and each od belongs to one sophead
I want to be able to return all od_items for a specific sophead like this:
all_od_items_for_first_sophead = Sophead.first.od_items
what is the correct association for getting all the od_items for a sophead?
I had tried:
has_many :od_items, through: :ods
but I believe that this is incorrect as it doesn't really match this diagram - in that diagram's example (with different model names), the arrow from patients to appointments would run the other direction.
Thanks in advance
Your try was correct way to do it.
Your models must have associations like this:
models/sophead.rb
class Sophead < ApplicationRecord
has_many :ods
has_many :od_items, through: :ods
end
models/od.rb
class Od < ApplicationRecord
belongs_to :sophead
has_many :od_items
end
models/od_item.rb
class OdItem < ApplicationRecord
belongs_to :od
end
Related
So currently i have in a app:
class Post < ApplicationRecord
belongs_to :category
end
and
class Category < ApplicationRecord
has_many :posts
end
which works fine as expected. However, i need to add multiple categories to a post . I thought about using has_many_and_belongs_to for each of them to acquire this but some trouble implementing this. It seems like a need to add an join table? If so, how would one look like with the setup shown above?
Any ideas?
I appreciate any input! Thanks in advance!
The table should be named categories_posts (categories comes first because of alphabetical sequence) and contain post_id and category_id integer columns (indexed, most probably). The it's as simple as:
class Post < ApplicationRecord
has_and_belongs_to_many :categories
end
class Category < ApplicationRecord
has_and_belongs_to_many :posts
end
You can create join table adding migration using:
rails g migration CreateJoinTableCategoryPost category post
Alternatively you can use has_many :through to have more control over a join table.
Advantages of using :through for many to many relationships
With has_many :through relationship you can have a model which will allow you to add validation, callbacks.
If you initially take some extra efforts to setup many to many relationship using through it can save a lot of time and headache in future
What if in future you want save the more information on join table like some custom sort, information about how the tables are associated which will not be allowed with has_and_belongs_to_many
Example
class Post < ApplicationRecord
has_many :categories, through: :post_categories
has_many :post_categories
end
class Category < ApplicationRecord
has_many :posts, through: :post_categories
has_many :post_categories
end
Adding relationship model with rails generator command
rails g model post_category category_id:integer post_id:integer custom:text
class PostCategory < ApplicationRecord
belongs_to :category
belongs_to :post
end
I have this associations:
class Ship < ApplicationRecord
has_many :captain_profiles
has_many :captains, through: :captain_profiles
end
class CaptainProfile < ApplicationRecord
belongs_to :captain
belongs_to :ship
end
class Captain < ApplicationRecord
has_one :captain_profile
has_many :schedules
end
class Schedule < ApplicationRecord
belongs_to :captain
end
And I need list of all ships that are ready to be taken to the sea. In other words I have to find all ships that has at least one Captain with at least one of his schedules.
I thought about merging two inner joins as I need Ships which has Captains which has Schedules.
I tried Captain.includes(:schedules).where("schedule.id IS NOT NULL") and so with Ships but it does not work. Could someone explain me what am I doing wrong and how shall I do this?
Simply use joins which generates INNER JOIN.
Ship.joins(captains: :schedules)
Hi I have 6 models with relations with each other:
Spell model which can have many tags and elements and one ring(aka level)
class Spell < ActiveRecord::Base
belongs_to :spell_ring
has_many :element_of_spells, dependent: :destroy
has_many :spell_elements, through: :element_of_spells
has_many :tag_of_spells, dependent: :destroy
has_many :spell_tags, through: :tag_of_spells
validates_presence_of :name
end
Spell element which can have many spells
class SpellElement < ActiveRecord::Base
has_many :element_of_spells, dependent: :destroy
has_many :spells, through: :element_of_spells
end
Spell tag, which can have many spells:
class SpellTag < ActiveRecord::Base
has_many :tag_of_spells, dependent: :destroy
has_many :spells, through: :tag_of_spells
end
Spell ring:
class SpellRing < ActiveRecord::Base
has_many :spells
end
And join models:
class ElementOfSpell < ActiveRecord::Base
belongs_to :spell
belongs_to :spell_element
end
class TagOfSpell < ActiveRecord::Base
belongs_to :spell
belongs_to :spell_tag
end
Ok now I want to put them to good use :)
What I know:
That if I take any spell_tag or spell_element or spell_ring object, I can get all associated spells.
element = SpellElement.first
spells_of_element = element.spells >> give me all associated spells
I know I can scope this with spell_ring_id since it is part of the spell object.
spell_ring = SpellRing.first
spells_of_element_and_ring = spells_of_element.where( spell_ring_id: spell_ring.id ) >> returns spells of given element and ring
What I don`t know:
How to scope spells_of_element_and_ring with given tag.
tag = SpellTag.first
spells_of_element_ring_and_tag = ??
Updated
What I want?
Is to be able to query spells:
by the spell_tags
by the spell_rings
by the spell_elements
and any combination of those three models.
It's a good idea, when posting to StackOverflow, to weed out as much code as possible, and really boil your question down to the simplest possible example. This will get quicker answers, and also be useful to more people.
Let's start out with the simpler example of a school, which has many classrooms, and each classroom has many students.
Let's create our models:
rails generate model school name:string
rails generate model classroom school_id:integer grade:integer
rails generate model student name:string classroom_id:integer
Now let's create our associations:
class School < ActiveRecord::Base
has_many :classrooms
has_many :students, through: :classrooms
end
class Classroom < ActiveRecord::Base
belongs_to :school
has_many :students
end
class Student < ActiveRecord::Base
belongs_to :classroom
end
Finally, we'll create three quick records:
school = School.create name: 'City Elementary'
classroom = school.classrooms.create grade: 4
student = classroom.students.create name: 'Bob'
Now we can get a list of all students at the school like so:
school.students
This works because a school has_many students, through classrooms.
I think what you actually want is a little more complicated - a spell can have many elements, and an element can belong to many spells. In this case, you need a "join table". Let's simplify your example by eliminating everything except spells and elements.
We start by creating our models:
rails generate model spell name:string
rails generate model element name:string
Now we create a join table, which keeps track of which spells and elements belong to each other:
rails generate migration create_elements_spells element_id:integer spell_id:integer
Now we define our associations (relationships):
class Element < ActiveRecord::Base
has_and_belongs_to_many :spells
end
class Spell < ActiveRecord::Base
has_and_belongs_to_many :elements
end
has_and_belongs_to_many automatically looks for a table with the combined name of the two models, in plural form, in alphabetical order. Now we can do things like:
spell = Spell.create name: 'set on fire'
flint = Element.create name: 'flint'
steel = Element.create name: 'steel'
spell.elements << flint
spell.elements << steel
Now, spell.elements lists both flint and steel. flint.spells will list our 'set on fire' spell. steel.spells will also list our spell. You can expand from there.
But what if you need to know more than just what element - what if you need to know how much? now you have extra data that doesn't belong in the Spell record or the Element record. It belongs on the association itself. We might call an element/amount combo an "ingredient", and create a table for it like so:
rails generate model ingredient spell_id:integer element_id:integer amount:string
And we update our associations:
class Element < ActiveRecord::Base
has_many :ingredients
has_many :spells, through: :ingredients
end
class Spell < ActiveRecord::Base
has_many :ingredients
has_many :elements, through: :ingredients
end
class Ingredient < ActiveRecord::Base
belongs_to :element
belongs_to :spell
end
Now we can add ingredients to our spell:
spell.ingredients.create element: flint, amount: '1 gram'
spell.ingredients.create element: steel, amount: '1 piece'
So spell.ingredients will list both flint and steel, and the amounts for each. This should get you well on your way to building you application.
#Jaime explain querying through the whole process very good. But I wanted my query to be more flexible.
I don't know it this is the Rails way. But I found something like this to fit me the best.
Because SpellElement.spells, SpellRing.spells, SpellTag.spells all returns an array. My idea is to just compare them and return only matched elements as a result.
So
spell_element_ring_and_tag = some_spell_element.spells & some_spell_ring.spells & some_spell_tag.spells
Will return only spell objects shared by all three arrays.
Sorry, this one is hard to phrase in the title. So here's what I'm trying to do. A workshop has many districts. Each district has exactly one district_contact (actually a district_contact_id). How can I use ActiveRecord to model the relationship between workshop and district_contact? I want to be able to do this:
Workshop.district_contacts
And get a collection of the actual user objects. Right now, I've done it using a short function:
def district_contacts
district_ids = []
self.districts.each do |district|
if district.contact_id
district_ids << district.contact_id
end
end
User.find(district_ids)
end
Define associations in the Workshop model:
has_many :districts
has_many :district_contacts, through: disctricts
Your model associations should look something like this.
class Workshop < ActiveRecord::Base
has_many :districts
has_many :district_contacts, through: disctricts
end
class District < ActiveRecord::Base
belongs_to :workshop
has_one :district_contract
end
I'm trying to get two has_many relationships from one model to another.
Specifically, I want:
class Driver < Active:Record::Base
has_many :reservations
has_many :requested_reservations
and
class Reservations < Active:Record::Base
belongs_to :driver
belongs_to :requester
The first one is a normal has_many/belongs_to relationship using driver_id on the reservations model.
But for the second one, I want to be able to call #driver.requested_reservations and #reservation.requester, and have it use the requester_id column in the Reservations class.
What do I need to put at the end of those has_many and belongs_to lines to get it to work properly?
I believe you can set the class and foreign key to get the desired results.
class Driver < Active:Record::Base
has_many :reservations
has_many :requested_reservations, class_name: 'Reservation', foreign_key: 'your_id'
...
end
class Reservations < Active:Record::Base
belongs_to :driver
belongs_to :requester, class_name: 'Driver', foreign_key: 'requester_id'
...
end
There are similar questions that have been asked before. See the following links for more information:
Rails multiple associations between two models
how to specify multiple relationships between models in rails using ActiveRecord associations