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
Related
I am trying to set up the model structure that has a User model Project model along with two join tables setup as has_many through to manage two specific aspects of the Project, ProjectManagers and ProjectMembers.
I can setup two has_and_belongs_to_many but it doesn't feel very railsy.
Right now, this is what I have and I'm unsure of how to proceed to use multiple has_many through (Project Manager, Project Member) both referencing User model.
Would a nested through be the way to go even if a Project Manager will not always be part of the Project User table?
project.rb
class Project < ApplicationRecord
has_many :project_members
has_many :users, through: :project_manager
end
user.rb
class User < ApplicationRecord
has_many :project_managers
has_many :users, through: :project_managers
end
project_manager.rb
class ProjectManager < ApplicationRecord
belongs_to :project
belongs_to :user
end
project_member.rb
class ProjectMember < ApplicationRecord
belongs_to :project
belongs_to :user
end
I don't see any problems with what you're doing. There are other options, but this approach should work as you want. Have you tried it? I'd do something like this.
class Project < ApplicationRecord
has_many :project_members
has_many :project_managers
has_many :members, through: :project_members, :class_name => User.to_s
has_many :managers, through: :project_manager, :class_name => User.to_s
end
Another approach, since the join tables are similar is to subclass them and add a type column to the join table. Not necessarily better than what you're doing.
You could also create a project_users table (don't separate members and managers) and include a "role" column. A scope on project_user.rb would bring back managers or members.
Personally, I would go with your approach. Managers will likely have different auth and have relationships with other objects. It's simpler to query and less likely to make a mistake.
And, I wouldn't recommend a has_and_belongs_to_many, you're likely to add other columns to the join table and you'll be glad you have the model.
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?
I have two models: Orders and Items. This is a many to many relationship, but I'm unsure if HMT is correct
Order model:
-user_id
-transaction_cost
-delivery_time
Item model:
-price
-name
Orders should be able to get all items in the order
Items do not need to be able to get all orders
The convention on this is to use the names of both models. A good name might be ItemOrders. Has many through is almost certainly a correct choice here.
class Order < ActiveRecord::Base
has_many :item_orders, dependent: :destroy
has_many :items, through: :item_orders
end
class Item < ActiveRecord::Base
has_many :item_orders, dependent: :destroy
has_many :orders, through: :item_orders
end
class ItemOrder < ActiveRecord::Base
belongs_to :item
belongs_to :order
end
Now you just have another ActiveRecord model, and you can add to it as you'd like. It will also be helpful for debugging. You can even use a model/scaffold generator to generate these:
rails g model item_order order:references item:references
This way you get the migrations correct right away. Nothing needs to be altered on your other models except for the above code.
So I'm Rails n00b and I want to create a "favorites" relationship such that a User can have many favorite Item. I'm not entirely sure how to do this, this is how I'm going to try but I'm not sure if this is a good practice at all:
class User < ActiveRecord::Base
has_many :favorites
//other code
end
class Favorite < ActiveRecord::Base
belong_to :user
has_one :item
end
class Item < ActiveRecord::Base
belongs_to :item
end
Is this a good way to do it? Should I be using has_and_belongs_to_many ?
I'm specially concerned in the following scenario: Say a user has 100 favorite items.
When I do a User.find(id) will I also be retrieving the 100 favorites and the 100 Items?
In case it's important: ruby version 1.9.3, rails version 3.2.11
Can you try has_many => :through?
class User < ActiveRecord::Base
has_many :favorites
has_many :items, :through => :favorites
//other code
end
In your case has_many :through is definitely the way to go. I would recommend reading: http://guides.rubyonrails.org/association_basics.html
Of particular interest with regard to your question:
2.8 Choosing Between has_many :through and has_and_belongs_to_many
Rails offers two different ways to declare a many-to-many relationship between models. The simpler way is to use has_and_belongs_to_many, which allows you to make the association directly:
class Assembly < ActiveRecord::Base
has_and_belongs_to_many :parts
end
class Part < ActiveRecord::Base
has_and_belongs_to_many :assemblies
end
The second way to declare a many-to-many relationship is to use has_many :through. This makes the association indirectly, through a join model:
class Assembly < ActiveRecord::Base
has_many :manifests
has_many :parts, :through => :manifests
end
class Manifest < ActiveRecord::Base
belongs_to :assembly
belongs_to :part
end
class Part < ActiveRecord::Base
has_many :manifests
has_many :assemblies, :through => :manifests
end
The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don’t need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you’ll need to remember to create the joining table in the database).
You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.
It is better than using has_and_belongs_to_many.
When I do a User.find(id) will I also be retrieving the 100 favorites
and the 100 Items?
No. You'll just get the user object.
Update:
Calling User.include(:favourites, :items).find(id) will get you joined tables in case you want to make many calls to items table from user object.
this seems pretty basic stuff here, but actually i'm finding it a bit harsh to define this scenario with Rails...
Perhaps any of you can provide some guidance?
So I have three Tables, Users, Invoices, and User_Invoice_Viewers (these basically map users that have viewer access to an invoice)
Now my models :
User.rb :
has_many :invoices
has_many :user_invoice_viewers
has_many :invoices, through :user_invoice_viewers
Invoice.rb
belongs_to user_invoice_viewers
belongs_to :user
User_Invoice_Viewers.rb
belongs_to :users
belongs_to :invoices
Now this just seems wrong... I repeat has_many :invoices on User model, so i expect conflict when executing : User.invoices ...
What would be the best solution for this? I had thought of putting it all on a user_invoice table, but since i expect to have more owners than viewers, for performance reasons, i decided to build a direct dependency between invoice and its owner...
Thanks
I would consider using the :class_name option on the association, so that the two relationships are named differently. Something like this:
class User < ActiveRecord::Base
has_many :invoices
has_many :user_invoice_viewers
has_many :viewable_invoices, through :user_invoice_viewers, :class_name => "Invoice"
...
end