has_many :through association cannot find a valid model - ruby-on-rails

I am creating association pretty much identical with the Rails Guides Patient-Appointment-Physician data model. A user has many prospects through prospect_subscription. However, when trying to access user.prospects in rails console, it throws the following error:
Rails couldn't find a valid model for Prospects association. Please provide the :class_name option on the association declaration. If :class_name is already provided, make sure it's an ActiveRecord::Base subclass. (NameError)
uninitialized constant User::Prospects (NameError)
Which is strange because all three models are right there. Migration has been run and sample data has been populated and can be checked in pgAdmin. Why can't Rails find the model?
Association defined at the models are as follows:
models/prospect.rb
class Prospect < ApplicationRecord
has_many :prospect_subscriptions
has_many :users, through: :prospect_subscriptions
end
models/user.rb
class User < ApplicationRecord
has_many :prospect_subscriptions
has_many :prospects, through: :prospect_subscriptions
end
models/prospect_subscription.rb
class ProspectSubscription < ApplicationRecord
belongs_to :user
belongs_to :prospect
end

I figured that wiping the database records clean and re-seeding helps. The difference is this time I assigned as user.prospects << [prospect_name], to make sure that the joins are created in the backend.

Related

Cannot destroy a model with a has_many through assocation

I have the following models linked through a has_many :through associaton. Below is the following models
class Campaign
has_many :email_notification_code_percentages, dependent: :destroy
has_many :email_notification_code_percentage_trackers, through: :email_notification_code_percentages
end
class EmailNotificationCodePercentage < ApplicationRecord
belongs_to :campaign
has_many :email_notification_code_percentage_trackers, dependent: :destroy
end
class EmailNotificationCodePercentageTracker < ApplicationRecord
belongs_to :email_notification_code_percentage
end
When I try execute the following #campaign.email_notification_code_percentage_trackers.destroy_all I get the following error:
Cannot modify association 'Campaign#email_notification_code_percentage_trackers' because the source reflection class 'EmailNotificationCodePercentageTracker' is associated to 'EmailNotificationCodePercentage' via :has_many. (ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection)
What is the issue here? This should be a simple has many through association as defined through the rails examples. What am I missing here? This looks correct to me.
This is the expected behavior, as you can read in the API docs (https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html):
An important caveat with going through has_one or has_many
associations on the join model is that these associations are
read-only.
Associations with the :through option or defined via the has_and_belongs_to_many macro are a bit special.
So, a couple of alternative ways:
Iterate over each elem:
# be careful if the association is very large, you can run into some memory or timeout issues
#campaign.email_notification_code_percentage_trackers.each(&:destroy)
Make the query a bit different to be able to use the destroy_all:
EmailNotificationCodePercentage.merge(#campaign.email_notification_code_percentage_trackers).destroy_all

Rails has_many through across workspaces ignores class_name

I'm working with a simple has_many through relationship between 2 models.
The Achievement model describes an achievement that can be conquered by a Professional::Company, through the ConqueredAchievement table, that holds when it was conquered.
As I am working acrosss namespaces, I know I have to supply the full class name when creating the relationship, but it doesn't seem to have any effect, as it throws the exact same error with or without the value passed to class_name.
Here's the code for the models:
class Achievement < ApplicationRecord
has_many :conquered_achievements
end
class Professional::Company < ApplicationRecord
has_many :conquered_achievements
has_many :achievements, through: :conquered_achievements
end
class ConqueredAchievement < ApplicationRecord
belongs_to :achievement
belongs_to :professional_company, class_name: 'Professional::Company'
end
When I try to create an association, by using:
c.conquered_achievements.create!(achievement: a)
I get the error (same error with and without the class_name on the ConqueredAchievement model):
ActiveModel::UnknownAttributeError (unknown attribute 'company_id' for ConqueredAchievement.)
On the generated table, the row is actually named professional_company_id.
I've seen a lot of complaints about has_many through not working properly across namespaces. Am I doing something wrong, or is this actually a bug?
Try:
class Professional::Company < ApplicationRecord
has_many :conquered_achievements, foreign_key: :professional_company_id
has_many :achievements, through: :conquered_achievements
end
The clue is in the error:
ActiveModel::UnknownAttributeError (unknown attribute 'company_id' for ConqueredAchievement.)
The association is trying to use an attribute named company_id on ConqueredAchievement to find the Professional::Company. But, as you say, that attribute doesn't exist.
Instead of letting rails try to infer the foreign key, you can stipulate the foreign key using the foreign_key: option as described in the docs, section 4.3.2.6.

What is the correct naming method for Rails models that represent a join table?

I have two classes (organisation.rb and user.rb) which are linked through a join table via ActiveRecord with a many to many relationship. A user can belong to many organisations, and an organisation can have many users.
Initially, I represented this relationship in this manner and this worked fine:
class Organisation < ActiveRecord::Base
has_many :members, through: :memberships, source: :user
has_many :memberships
end
class User < ActiveRecord::Base
has_many :organisation_members, through: :memberships, source: :organisation
has_many :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :organisation
end
I asked a friend to review my pull request and he suggested that the Rails convention is to name the join table with a lexical ordered list of the tables to be joined. I searched and confirmed this:
Rails naming convention for join table
As such, I reverted my migrations and started anew, with the new relationships:
class Organisation < ActiveRecord::Base
has_many :members, through: :organisations_users, source: :user
has_many :organisations_users
end
class User < ActiveRecord::Base
has_many :organisation_memberships, through: :organisations_users, source: :organisation
has_many :organisations_user
end
class OrganisationsUser < ActiveRecord::Base
belongs_to :user
belongs_to :organisation
end
Having made these changes, a number of my tests failed due to not recognising the model 'organisation_user.rb'. I then tried 'organisations_users.rb', and also no luck. This was due to my ignorance of rails' pluralisation, and therefore naming the join table model correctly resulted in all tests passing. As described above, the correct naming is:
class OrganisationsUser
This results in a rather strange model name of a pluralised organisation followed by a singular user.
I understand that this is technically correct. The model name should singular, however it isn't particularly easy to understand when compared with the 'memberships' join table.
What is the correct way to handle this - should I revert back to memberships, find a way to override Rails' default pluralisation, or continue with the awkwardly named status quo?

Rails: trying to create a has_many relationship from two tables to one table

I have two models with associated tables (villa_rooms & homes). Both have rates associated with them. I would like to use just one "rates" model with associated table to store rates for both villa_rooms and houses.
I have created the following for villa_rooms, but it is not finding associated rates:
class VillaRoom < ActiveRecord::Base
has_many :villa_room_rates
has_many :rates, :through => :villa_room_rates
end
class VillaRoomRates < ActiveRecord::Base
belongs_to :villa_room
belongs_to :rate
end
class Rate < ActiveRecord::Base
has_one :villa_room_rate
has_one :villa_room, :through => :villa_room_rates
end
When I try to run VillaRoom.find(1).rates I get the following error
NameError: uninitialized constant VillaRoom::VillaRoomRate
Any help is greatly appreciated.
Thanks!
I think is a spelling typo in your class VillaRoomRates, try with singular.
If you want the default behavior from Rails, all your model names should be in singular.
But the tables will be created with plural names.

rails linked tables "uninitialized constant" error

I'm building what I thought was a fairly simple recipe app while learning RoR.
I've got a table for users, and a table for recipes, and a recipe_users table where a user is saving a list of recipes.
The error I'm getting from rails is "uninitialized constant User::RecipeUser"
My Models are as follows
class User < ActiveRecord::Base
acts_as_authentic
has_many :recipe_users
has_many :recipes, :through = > :recipe_users
end
class Recipes < ActiveRecord::Base
has_many :ingredients, :dependent => :destroy
has_many :recipe_users
has_many :users, :through => :recipe_users
end
class RecipeUsers < ActiveRecord::Base
belongs_to :user
belongs_to :recipe
end
now in my users controller, I am attempting to call
#user = User.find(current_user.id)
#userRecipes = #user.recipes.find()
looking at my mysql Show Tables, I get
recipe_users
recipes
schema_migrations
user_sessions
users
so as far as I can tell, I've got the naming conventions right.
Any suggestions as to why I'm getting this error?
It looks like this was an issue of naming conventions.
I deleted all references to recipe_users and recreated the table, model and controller as meals.
Not a great name, but it all came together without a hitch.
I've never liked the naming conventions that rails seems to expect I think in part due to the pluralization without actually informing the developer of what name rails may be expecting.

Resources