I'm getting the error ActiveRecord::InverseOfAssociationNotFoundError (Could not find the inverse association for sponsorships (:sponsor_type in LegiscanModel::Sponsorship)
when importing records using sidekiq. Below are my models.
sponsorship.rb
class LegiscanModel::Sponsorship < ApplicationRecord
belongs_to :bill, class_name: 'LegiscanModel::Bill', foreign_key: 'bill_id', inverse_of: :sponsorships
belongs_to :sponsor, class_name: 'LegiscanModel::Politician', foreign_key: :politician_id, inverse_of: :sponsorships
accepts_nested_attributes_for :sponsor
delegate :full_name, to: :sponsor, prefix: true, allow_nil: true
validates :politician_id, uniqueness: { scope: :bill }
belongs_to :sponsorship_type, class_name: 'LegiscanModel::SponsorType', foreign_key: :sponsor_type_id, inverse_of: :sponsorships
end
sponsor_type.rb
class LegiscanModel::SponsorType < ApplicationRecord
has_many :sponsorships, class_name: 'LegiscanModel::Sponsorship', inverse_of: :sponsor_type, dependent: :destroy
end
politician.rb
has_many :sponsorships, dependent: :destroy, inverse_of: :sponsor, class_name: 'LegiscanModel::Sponsorship'
sidekiq job(partial)
def handle_sponsors(sponsors, bill_id)
sponsors.each do |sponsor|
LegiscanModel::Politician.find_by(people_id: sponsor['people_id']).tap do |politician|
LegiscanModel::Sponsorship.find_or_create_by!(politician_id: politician.id, bill_id: bill_id, sponsor_order: sponsor['sponsor_order'], sponsor_type_id: sponsor['sponsor_type_id'])
end
end
end
CodePudding user response:
If you actually set your classes up properly with explicit nesting instead of using the scope resolution operator you can dramatically improve this code:
module LegiscanModel
class Sponsorship < ApplicationRecord
belongs_to :bill
belongs_to :sponsor,
class_name: 'Politician', # specifying the module is optional
inverse_of: :sponsorships
belongs_to :sponsorship_type
accepts_nested_attributes_for :sponsor
delegate :full_name, to: :sponsor, prefix: true, allow_nil: true
# should be the database column since its used to create a query
validates :politician_id, uniqueness: { scope: :bill_id }
end
end
module LegiscanModel
class SponsorshipType < ApplicationRecord
has_many :sponsorships, dependent: :destroy
end
end
While this might seem like a trivial stylistic choice its really not - by using module LegiscanModel
you're reopening the module and setting the module nesting so that you can refer to constants in the same namespace.
This also avoids autoloading errors and bugs due to suprising constant lookup. ::
should only be used when refering to constants - not when defining them.
You also do not need to specify the foreign key option when it can be derived from the name of the assocation. While there is no harm in it but its extra noise. Rails can also automatically infer the inverses. You can check it with if you want to through the assocation reflexion:
LegiscanModel::Sponsorship.reflect_on_assocation(:sponsorship_type)
.inverse