I have 3 models: Company, Portable and Jobsite. I want to be able to call company.portables, company.jobsites, portable.company, portable.jobsite, jobsite.portables and jobsite.company. I've setup my associations like this:
class Company
has_many :portables
has_many :jobsites, through: :portables
end
class Portable
belongs_to :company
belongs_to :jobsite
end
class Jobsite
has_many :portables
has_one :company, through: :portables
end
I can call everything successfully except jobsite.company. When I make this call, I get:
ActiveRecord::HasOneThroughCantAssociateThroughCollection: Cannot have a has_one :through association 'Jobsite#company' where the :through association 'Jobsite#portables' is a collection. Specify a has_one or belongs_to association in the :through option instead.
What is the proper way to setup these associations? Do I have to make the jobsite to company association a belongs_to :company on the jobsite and add the jobsite_id to the company? It seems there should be another way to achieve this with what I have already setup.
If you think about it, the schema as described just can't work. If you really want Jobsite to have only one Company, then it can't go through Portable, bevause Jobsite has many Portables.
So your left with two choices:
- If what you mean is one Jobsite should have a 'main' company, then have a Jobsite has_one :company directly, without the :through part.
- If what you mean is thay Jobsites can really have many Companies through their many Portables, then just change your code to have_many:.
Bonus choice, you can also get fancy with the options here by doing something like:
has_one :company, -> {where(primary: true)}
You can do all kinds of fancy stuff that way.
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 these models in my Rails app:
group.rb
has_many :group_members
has_many :group_foo_types
group_member.rb
# fields
:group_id, :user_id
belongs_to:group
has_many :group_foo_types, :through=>:groups
has_one :user
has_many :foo, :through=>:user
has_many :bar, :through=>:user
group_foo_type.rb
# fields
:group_id, :foo_type_id
belongs_to :group
belongs_to :foo_type
user.rb
has_many :foo
has_many :bar
foo.rb
# fields
:user_id, :foo_type_id
belongs_to :user
belongs_to :foo_type
bar.rb
# fields
:user_id, :foo_type_id
belongs_to :user
belongs_to :foo_type
What I'm trying to do is, for a group, find the foos and bars per user taking into account the foo_type_id for the group (from group_foo_type).
However, I'm not sure if I have this modeled correctly.
From this, it seems like I can do group.group_members.user to get the users and then user.foos' and 'user.bars to get a user's foos and bars. This doesn't take into account the foo_type_id for the group though (from group_foo_type).
Can someone please help recommend an approach to accomplish what I want to do? Thank you!
As mentioned in this answer, since Rails 3.1 you can nest has_many :through associations. So to have easy access to all of a group's foos and bars, you could add the following associations:
# group.rb
has_many :users, through: :group_members
has_many :foos, through: :users
has_many :bars, through: :users
With this, group.foos would give you all of the group's foos (via group_member and user).
The association methods in rails can be used with the various finder methods, so to limit the foos by group_foo_type:
group_foos = group.foos.where(foo_type_id: group.group_foo_type_ids)
(group_foo_type_ids being another useful association method, check the Association Basics guide for more info.)
A caution: there are a lot of steps that have to go on in the background to achieve any of this stuff, even if it does get relatively easy to actually code. You may want to keep an eye on the queries that get generated (either through the console or logs) and how they perform, as they're likely to get fairly involved. It looks like you have a fairly complex set of associations there, so it may be worth looking at whether you can simplify anything there. Just something to keep in mind!
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
I am trying to create an association between two tables. A student table and a computer table.
A computer can only ever be assigned to one student (at any one time) but a student can be assigned to multiple computers.
This is what I currently have in mind. Setting up a has-many through relationship and modifying it a bit.
class Student < ActiveRecord::Base
has_many :assignemnts
has_many :computers, :through => :assignments
end
class Computer < ActiveRecord::Base
has_one :assignment
has_one :student, :through => :assignments
end
class Assignment < ActiveRecord::Base
belongs_to :student
belongs_to :computer
end
Does this seem like the best way to handle this problem? Or something better sound out quickly to the experts here. Thanks!
You need first to decide if a simple one-to many relationship is enough for you.
If yes, it gets a lot easier, because you can get rid of the Assignment-class and table.
Your database-table "computers" then needs a student_id column, with a non-unique index
Your models should look like this:
class Computer < ActiveRecord::Base
belongs_to :student
end
class Student < ActiveRecord::Base
has_many :computers, :dependent => :nullify
end
"dependent nullify" because you don't want to delete a computer when a student is deleted, but instead mark it as free.
Each of your computers can only be assigned to a single student, but you can reassign it to a different student, for example in the next year.
Actually your approach is fine, as one offered by #alexkv. It is more discussion, than question.
Another thing if you want to use mapping table for some other purposes, like storing additional fields - then your approach is the best thing. In has_many :through table for the join model has a primary key and can contain attributes just like any other model.
From api.rubyonrails.org:
Choosing which way to build a many-to-many relationship is not always
simple. If you need to work with the relationship model as its own
entity, use has_many :through. Use has_and_belongs_to_many when
working with legacy schemas or when you never work directly with the
relationship itself.
I can advise you read this, to understand what approach better to choose in your situation:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
http://blog.hasmanythrough.com/2006/4/20/many-to-many-dance-off
You can also use has_and_belongs_to_many method. In your case it will be:
class Student < ActiveRecord::Base
has_many :assignemnts
has_and_belongs_to_many :computers, :join_table => 'assignments',
end
class Computer < ActiveRecord::Base
has_one :assignment
has_and_belongs_to_many :student, :join_table => 'assignments',
end
or you can rename assignments table to computers_students and remove join_table
class Student < ActiveRecord::Base
has_many :assignemnts
has_and_belongs_to_many :computers
end
class Computer < ActiveRecord::Base
has_one :assignment
has_and_belongs_to_many :student
end
My situation is like this.
Company has many users and users may belongs to many companies.
And current implementation is something like below.
class Company
has_many :employments
has_many :users, :through => :employments
end
class Employment
belongs_to :company
belongs_to :user
end
class User
has_many :employments
has_many :companies, :through => :employments #This doesn't looks correct
end
It works, but "user has many companies" doesn't looks logically meaningful. It must be some thing like belongs_to_many companies.
Do I need to use has_and_belongs_to_many?
Can some one please suggest the right way for representing these relationships?
If you are not going to add some special behaviour to Employment class and you don't really need it, then you should propably better use has_and_belongs_to_many. This way it will still have some table called CompanyUser, but there will not be any such class in the code.
Added:
There are just 3 possbile conections between 2 objects: 1-to-1, 1-to-many and many-to-many. Rails indicates with has_* or belongs_to what of the 2 objects will get a foreign key. So:
1-to-1:
has_one and belongs_to are used. The object with belongs_to gets the FK.
1-to-many:
has_many and belongs_to are used. The object with belongs_to gets the FK.
many-to-many:
has_many[through] or has_and_belongs_to_many are used in both objects. No-one gets a FK, instead a ConnectionTable is used.
So there is no such thing as belongs_to_many, because it would be the same as has_many - providing the same relation 1-to-many.