Rails: ActiveRecord has_many association not working - ruby-on-rails

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

Related

Rails has_many through - query through the relationship

Running rails 6.0.3.2 / ruby 2.7.1
I have a has_many through-relationship that isn't behaving as expected.
My models look like this:
class Item < ApplicationRecord
has_many :item_permissions
has_many :users, :through => :item_permissions
end
class User < ApplicationRecord
has_many :item_permissions
has_many :items, :through => :item_permissions
end
class ItemPermission < ApplicationRecord
belongs_to :items
belongs_to :users
end
Now I want to retrieve all Items that a certain User has permission to:
u = User.find(1)
u.items
gives me an error:
NameError (uninitialized constant User::Items)
I can get the permission entries with
u.item_permissions
Is there any way to retrieve the items for a certain user or also the other way round receiving all users that are linked to a specific item?
The mistake is in your belongs_to declarations, in both your are using the plural word instead of the singular one to reference the associated model. Try with:
class ItemPermission < ApplicationRecord
belongs_to :item # :item instead of :items
belongs_to :user # :user instead of :users
end
More information here.

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

Using rails counter_cache on a non-standard has_many :through

so the standard way to use a has_many :through association would be to use the Physician-Appointment-Patient model from the Active Record Association Guide, right? It's basically a more verbose HABTM, with two models having a has_many :through and the connector model having two belongs_to. Now, in my current project I have a model structure like this:
class Cart < ActiveRecord::Base
has_many :line_items, through: line_item_groups
has_many :line_item_groups
end
class LineItemGroup < ActiveRecord::Base
belongs_to :cart
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :line_item_group
has_one :cart, through: line_item_group
end
Works fine. But now I want a line_item_count on Cart and can't figure out where I should add the counter_cache attribute.
Any help is appreciated :-)
First add line_item_count field in carts table, then in LineItem model add
class LineItem < ActiveRecord::Base
before_create :increment_counter
before_destroy :decrement_counter
def increment_counter
Cart.increment_counter(:line_item_count, cart.id)
end
def decrement_counter
Cart.decrement_counter(:line_item_count, cart.id)
end
end
I didn't tried it, but I think it will solve your problem.

Rails has_many :though STI

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

Nested models with has_many in Rails

For example I have in my app model User, that has_many models Post. And Post has_many Attachment. So I can do this
user.posts
and this
post.attachment
But what if I want do smth like
user.attachments
Is there any build-in solution for this?
You would use a has_many through association. You should end up with something similar to the following structure:
class User < ActiveRecord::Base
has_many :posts
has_many :attachments, :through => :posts
end
class Post < ActiveRecord::Base
has_many :attachments
end
class Attachments < ActiveRecord::Base
belongs_to :posts
end
The relevant section from the above link:
The has_many :through association is also useful for setting up “shortcuts” through nested has_many associations...

Resources