Rails has_many :though STI - ruby-on-rails

Lets start with code that may go like this:
class User < ActiveRecord::Base
has_many :photos
has_many :submitted_photos
has_many :posted_photos
has_many :handshakes, through: :photos
end
class Photo < ActiveRecord::Base
belongs_to :user
end
class PostedPhoto < Photo
has_many :handshakes, inverse_of: :poster, foreign_key: 'poster_id'
end
class SubmittedPhoto < Photo
has_many :handshakes, inverse_of: :submitter, foreign_key: 'submitter_id'
end
class Handshake < ActiveRecord::Base
belongs_to :submitter, class_name: 'SubmittedPhoto'
belongs_to :poster, class_name: 'PostedPhoto'
end
The STI part and associations between photos and handshakes work fine. The problem is getting all user's handshakes through his photos.
Using the code above Rails will obviously complain that model Photo does not have an association called handshakes. Is there any facility in AR that would allow me to specify a relation of this kind? If not, what query would you write to get them?

You could use in User:
has_many :submitted_handshakes, through: :submitted_photos, source: :handshakes
has_many :posted_handshakes, through: :posted_photos, source: :handshakes
I understand this works as is you had:
user.submitted_handshakes ----> user.submitted_photos.handshakes
Obviously handshakes method does not exist in submitted_photos, but AR made this for you by joining tables.

I ended up using the following code, looked like an easiest option to solve this problem.
class User
def handshakes
Handshake.where('submitter_id IN (?) OR poster_id IN (?)', submitted_photo_ids, posted_photo_ids)
end
end

Related

Many-to-many association with join table and multiple aliases

I'm working on a geocaching application where users can create a new cache or visit an existing one. Here are the models:
class User < ApplicationRecord
has_many :usercaches
has_many :visited_caches, source: :caches, through: :usercaches
has_many :created_caches, class_name: :caches
end
class Cache < ApplicationRecord
has_many :usercaches
has_many :visitors, source: :users, through: :usercaches
end
class Usercache < ApplicationRecord
belongs_to :user
belongs_to :cache
end
The join table looks the way it does because I've been trying to eliminate any potential errors related to capitalization or pluralization. Whenever I create a User in Rails console and try to look at new_user.visited_caches, I get the following error:
ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not
find the source association(s) :caches in model Usercache. Try
'has_many :visited_caches, :through => :usercaches, :source =>
'. Is it one of user or cache?
When I rearrange the association as suggested, however, the error remains the same. Is there some small detail I'm missing, or am I working with a completely incorrect paradigm for what I want to accomplish?
source must be provided in a singular form (the documentation for has_many provides an example for it):
class User < ApplicationRecord
has_many :usercaches
has_many :visited_caches, source: :cache, through: :usercaches
has_many :created_caches, class_name: :caches
end
class Cache < ApplicationRecord
has_many :usercaches
has_many :visitors, source: :user, through: :usercaches
end
class Usercache < ApplicationRecord
belongs_to :user
belongs_to :cache
end

Structuring associations Rails 4

This is probably a fairly straight forward answer that I feel like I should know, but occasionally I run across something like this that stumps me.
I'm working on a rails app that requires me to essentially create a leasing system for rentals.
I've got a user, a building, a lease and an unit. The way I've got it structured right now is:
class Buildings < ActiveRecord::Base
has_many :units
has_many :users
end
class User < ActiveRecord::Base
belongs_to :buildings
has_many :units, through :lease
end
class Lease < ActiveRecord::Base
belongs_to :user
belongs_to :unit
end
class Unit < ActiveRecord::Base
belongs_to :building
belongs_to :user
has_one :lease
end
I'm running into syntax errors and association errors and the documentation is as clear as mud. Perhaps someone can help me to properly structure these associations.
Your syntax error is on the User class
Change
has_many :units, through :lease
to
has_many :unit, through: :lease
or
has_many :units, :through => :lease

Specifying Conditions on Multiple Nested Eager Loaded Associations

I'm trying to query on ActiveRecord multiple nested eager loaded associations with conditions like so:
user.books.includes(slot: [room: :school]).where("books.slot.room.school.id = 1")
Obviously this query is wrong, but basically what I'm trying to reach is a relation of user.books for a certain school.
My model structure is:
class User < ActiveRecord::Base
has_many :books
has_many :slots, through: :books
has_many :rooms, through: :slots
has_many :schools
end
class Book < ActiveRecord::Base
belongs_to :user
belongs_to :slot
end
class Slot < ActiveRecord::Base
has_many :books
belongs_to :room
end
class Room < ActiveRecord::Base
belongs_to :school
has_many :slots
end
class School < ActiveRecord::Base
has_many :users
has_many :rooms
has_many :slots, through: :rooms
has_many :books, through: :slots
end
Any ideas? Thanks in advance.
includes eager loads association records, while joins does what you want to do.
This question has been asked here numerous times. Please make sure that you look and try to find a similar question here before you ask one.
So you want to change your code like this:
user.books.joins(slot: [room: :school]).where(schools: { id: 1 })

issue with has_many, through association

For some reason, my has_many through association isn't working. Here are my models:
class Interest < ActiveRecord::Base
has_many :evints
has_many :events, through: :evints
has_many :images, through: :events
end
class Event < ActiveRecord::Base
has_many :evints
has_many :images
has_many :interests, through: :evints
end
class Evint < ActiveRecord::Base
belongs_to :events
belongs_to :interests
end
The Evints table has three columns: interest_id, event_id, and id.
When I call #interest.events, I get the error message
uninitialized constant Interest::Events
Obviously, there's something going wrong with the association if #interest.events is being read as a constant!
Does anyone have any ideas?
Thanks!
Check your Evint class, it should be:
class Evint < ActiveRecord::Base
belongs_to :event
belongs_to :interest
end
On a different note, I think Evint isn't really a very good name. It'd suggest that you go with EventInterest, and name the table event_interests.

Rails: ActiveRecord has_many association not working

I'm a bit new to Rails Active Record associations. I've tried to set up a relationship, but I get ActiveRecord error when I try to retrieve data. Did I associate my models incorrectly?
User has many Uploads, which has many UserGraphs:
class User < ActiveRecord::Base
has_many :uploads, through: :user_graphs
end
class Upload < ActiveRecord::Base
has_many :users, through: :user_graphs
end
class UserGraph < ActiveRecord::Base
belongs_to :user
belongs_to :upload
end
I want to get all of a user's uploads, and all of a user's graphs. The 2nd line doesn't work in rails console and gives an error
#user = User.find(1)
#uploads = #user.uploads
The error:
ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association :user_graphs in model User
Extra Credit:
If Users have Uploads that have UserGraphs... shouldn't it be has_many :uploads and has_many :user_graphs, through :uploads?
Add
has_many :user_graphs
to the User and Upload classes.
The :through option defines a second association on top of this one.
You didn't tell Rails that you have a user_graphs association on User, only an uploads association. So when Rails goes to follow the user_graphs association on uploads, it can't find it.
So, you need add the user_graphs association. Your models should look like this:
class User < ActiveRecord::Base
has_many :user_graphs # <<< Add this!
has_many :uploads, through: :user_graphs
end
class Upload < ActiveRecord::Base
has_many :user_graphs # <<< Add this!
has_many :users, through: :user_graphs
end
class UserGraph < ActiveRecord::Base
belongs_to :user
belongs_to :upload
end

Resources