How to use a named_association for has_many_through relationship? - ruby-on-rails

How to write this:
has_many :sales, foreign_key: :buyer_id, dependent: :destroy
has_many :purchased_books, class_name: 'Book', through: :sales, source: :book
as this:
has_many :purchases, class_name: 'Sale', foreign_key: :buyer_id, dependent: :destroy
has_many :purchased_books, class_name: 'Book', through: :sales, source: :book
It gives me the following error at the moment:
Could not find the association :sales in model User (ActiveRecord::HasManyThroughAssociationNotFoundError)
And writing it has_many :sales is grammatically incorrect when foreign_key: :buyer_id.

The :through key must reference an association that is defined.
has_many :purchases, class_name: 'Sale', foreign_key: :buyer_id, dependent: :destroy
has_many :purchased_books, class_name: 'Book', through: :purchases, source: :book

Related

Undefined method `create' for nil:NilClass

I am creating a project but whenever i trying to create grade for Class student using current_user.grade.create() or current_user.create_grade() getting an error "undefined method create for nil class"
My code is as follows.
Student.rb
class Student < User
has_one :user_grade , dependent: :destroy, foreign_key: 'user_id'
has_one :grade , through: :user_grade
end
Grade.rb
class Grade < ApplicationRecord
has_many :user_grades, dependent: :destroy
has_many :admins, through: :user_grades
has_many :teachers, through: :user_grades
has_many :students , through: :user_grades
has_many :guardians, through: :user_grades
has_many :posts, dependent: :destroy
validates_presence_of :cls
end
user_grade.rb
class UserGrade < ApplicationRecord
belongs_to :grade
belongs_to :admin, optional: true, class_name: 'Admin', foreign_key: 'user_id'
belongs_to :teacher, optional: true, class_name: 'Teacher', foreign_key: 'user_id'
belongs_to :student, optional: true, class_name: 'Student', foreign_key: 'user_id'
belongs_to :guardian, optional: true, class_name: 'Guardian', foreign_key: 'user_id'
end
Try using current_user.grade = Grade.new({}). Then calling current_user.save will save the association and save the new Grade.

Product and Category relationship

Trying to have following relationship in my application.
Product can belong to many categories, sub categories and sub sub categories.
Current design:
Product:
has_many :categorizations, dependent: :destroy
has_many :categories, through: :categorizations
has_many :sub_categories, through: :categorizations
has_many :sub_sub_categories, through: :categorizations
Category:
has_many :categorizations
has_many :products, through: :categorizations
has_many :sub_categories, class_name: 'Category', foreign_key: 'parent_id'
belongs_to :parent_category, class_name: 'Category', foreign_key: 'parent_id'
Categorization:
belongs_to :category
belongs_to :sub_category, class_name: 'Category', foreign_key: 'sub_category_id'
belongs_to :sub_sub_category, class_name: 'Category', foreign_key: 'sub_sub_category_id'
belongs_to :product
products of a particular category can be listed as category.products.
How to access products of a particular sub_category and sub_sub_category?
What changes should I make??
Add this line has_many :sub_sub_categories, through: :sub_categories to the Product model.
## app/models/product.rb
has_many :sub_categories
has_many :categories, through: :sub_categories
has_many :sub_sub_categories, through: :sub_categories
If I were you, I would design like this:
Product:
has_many :categorizations
has_many :categories, through: :categorizations
Categorization:
belongs_to :product
belongs_to :category
Category:
belongs_to :parent, class_name: 'Category', optional: true
has_many :children, class_name: 'Category', foreign_key: :parent_id, dependent: :nullify
has_many :categorizations
has_many :products, through: :categorizations
Note: Add parent_id to table categories

ActiveRecord has many through with both being polymorphic

I have the association setup like the below, but I'm getting errors/exceptions thrown from Rails telling me that I don't have the associations set up properly.
Here's what I have:
class Case
has_many :case_accesses, as: :policy, inverse_of: :case, dependent: :destroy
has_many :agents, through: :case_accesses, source: :ownable, source_type: 'Agent'
end
class CaseAccess
belongs_to :policy, polymorphic: true
belongs_to :ownable, polymorphic: true
end
class Agent
has_many :case_accesses, as: :ownable, dependent: :destroy
has_many :cases, through: :case_accesses
end
The error from Rails:
Could not find the source association(s) "case" or :cases in model CaseAccess. Try 'has_many :cases, :through => :case_accesses, :source => '. Is it one of policy, connection, or ownable?
I tried setting the source to ownable and it's causing problems in my query. How should I be setting up this association? It's a traditional has many through, except on one side the policy can be either of type Case or Ppae, and the ownable can be of type Agent or User.
Columns for tables:
Case
-id
CaseAccess
-id
-policy_id
-policy_type
-ownable_id
-ownable_type
Agent
-id
A Case has many case_accesses as policy. Is the CaseAccess the policy, or is the Case the policy for the CaseAccess?
I believe you need to change
has_many :case_accesses, as: :policy, inverse_of: :case, dependent: :destroy
to
has_many :case_accesses, inverse_of: :policy, dependent: :destroy
This is what did it:
class Case
has_many :case_accesses, as: :policy, inverse_of: :policy, dependent: :destroy
has_many :agents, through: :case_accesses, source: :ownable, source_type: 'Agent'
has_many :users, through: :case_accesses, source: :ownable, source_type: 'User'
end
class CaseAccess
belongs_to :policy, polymorphic: true
belongs_to :ownable, polymorphic: true
end
class Agent
has_many :case_accesses, as: :ownable, dependent: :destroy
has_many :cases, through: :case_accesses, source: :policy, source_type: 'Case'
end

Could not find the source association(s) :followed in model Relationship.

I am following Michael Hartl's rails book and am getting the following error message:
ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :follower in model Relationship. Try 'has_many :following, :through => :active_relationships, :source => <name>'. Is it one of ?
when calling michael.follower (where michael is a user object).
Here are the associations:
class User < ActiveRecord::Base
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :following, :through => :active_relationships, source: :follower
has_many :followers, :through => :passive_relationships, source: :followed
end
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User", foreign_key: "follower_id"
belongs_to :followed, class_name: "User", foregin_key: "followed_id"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
If I understand correctly, you have many-to-many relation between User and Relationship models through relationship-model (I'm sorry for tautology).
I suggest to change
has_many :following, :through => :active_relationships, source: :follower
has_many :followers, :through => :passive_relationships, source: :followed
to
has_many :following, :through => :active_relationships, class_name: "User"
has_many :followers, :through => :passive_relationships, class_name: "User"

Rails has_many :through --> How do you set up an association to pull from multiple join models?

I'd like to set up multiple has_many :through relationships in parallel. Here are my 2 standard and 2 join models:
User.rb
has_many :ownerships, dependent: :destroy
has_many :devices, through: :ownerships
has_many :bookings, dependent: :destroy
has_many :devices, through: :bookings
Ownership.rb
belongs_to :user, touch: true, counter_cache: :devices_count
belongs_to :device, touch: true
Booking.rb
belongs_to :user, touch: true, counter_cache: :bookings_count
belongs_to :device, touch: true, counter_cache: :bookings_count
Device.rb
has_many :ownerships, dependent: :destroy
has_many :users, through: :ownerships
has_many :bookings, dependent: :destroy
has_many :users, through: :bookings
This current setup is NOT working as expected, there seems to be crosstalk between the join models. I want the join models to be independent and in parallel (i.e. Users can have relationships - Ownerships - with devices independently of being able to book them). I am not looking for a nested has_many :through relation here.
When I change the User Ownerships of a Device that seems to alter the number of Bookings and vice versa... any ideas on how should I be setting this up correctly?
I think the first error you've got is you're calling two associations by the same name (users / devices)
To help any further respondents, the real question is --> how do you set up an association to pull from multiple join models?
Quick Fix
Rails associations are named primarily by their class, but because of conflicts, you should refrain from setting them twice. This is why you're seeing the current issue. A simple resolution will be to call the associations by different names:
User.rb
has_many :ownerships, dependent: :destroy
has_many :owner_devices, through: :ownerships, class_name: "Device", foreign_key: "ownership_id"
has_many :bookings, dependent: :destroy
has_many :booking_devices, through: :ownerships, class_name: "Device", foreign_key: "booking_id"
I am still looking for information on how you could set an association to use two models
This appears be a working solution following Rich Peck's suggestions:
User.rb
has_many :ownerships, dependent: :destroy
has_many :device_ownerships, through: :ownerships, class_name: "Device", foreign_key: "device_id", source: :device
has_many :bookings, dependent: :destroy
has_many :device_bookings, through: :bookings, class_name: "Device", foreign_key: "device_id", source: :device
Booking.rb (Join model)
belongs_to :user, touch: true, counter_cache: :bookings_count
belongs_to :device, touch: true, counter_cache: :bookings_count
Ownership.rb (Join model)
belongs_to :user, touch: true, counter_cache: :devices_count
belongs_to :device, touch: true, counter_cache: :users_count
Device.rb
has_many :ownerships, dependent: :destroy
has_many :user_ownerships, through: :ownerships, class_name: "User", foreign_key: "user_id", source: :user
has_many :bookings, dependent: :destroy
has_many :user_bookings, through: :bookings, class_name: "User", foreign_key: "user_id", source: :user
To be honest, I'm a bit confused over why the foreign_key's need(?) to be set up as they are, so I'll have to do a bit more reading about it. Otherwise it appears to be functional, I don't see crosstalk between these join models anymore.

Resources