I'm struggling to understand model associations, and how to have them set up correctly. I'm hoping someone here knows how, and that I can kind of explain what I want to accomplish.
I have a user model that looks like this:
has_many :companies
has_many :contacts
belongs_to :company
a user can create a company, and belongs to a company. It can also import contacts from email providers (such as Google). Thus it can have many contacts.
Then we have the company model:
belongs_to :admin, class_name: 'User', foreign_key: 'user_id'
has_many :users
has_many :categories
has_many :contacts
a company belongs to an admin, which is the user that creates it. It can also however have several users that belongs to the company (they get invited by the admin). It also has categories, that the admin can create - which belongs to the company. It can also have contacts, which will be put the content of the categories (the categories are groups for contacts).
Then we have the categories. They are created by the admin (user) for the company.
belongs_to :company
has_many :contacts
The categories store all the contacts.
Then lastly the contacts:
belongs_to :user
belongs_to :company
The contacts belongs to the user (the user that imports them) however they are also a part of the assets to the company (as all the actions the user makes), they don't necessarily need to be a part of a category, however they can - and can also be a part of several categories.
I'm certain there are some flaws, and I want to fix my associations now so I don't end up building a lot of code on the wrong structure.
Hope somebody can help me out, on how to clean up this relatively large app, for a rookie
I'd change a few things:
In the company model, change has_many :contacts to has_many :contacts, through: :categories. It seems like categories only serve to group contacts together. And so therefore:
In the contact model, add belongs_to :category and remove belongs_to :company
Users and contacts do not have to be associated if a user wants to import contacts. I would either remove this association entirely if you don't need it or have a the same has_many :categories and has_many :contacts, through: :categories relationship as the companies. Of course, remove belongs_to :user in the contacts.
You might look into polymorphism. I'm doing something like this although I stripped out and and renamed a few things to more closely match your question. This is untested but might help illustrate a different approach
class User < ActiveRecord::Base
has_many :business_users
has_many :businesses, :through => business_users
has_many :contacts, :through => :contact_users
...
end
class Contact < ActiveRecord::Base
belongs_to :user
belongs_to :company
has_many :categorizations, :as => :categorizeable
has_many :categories, -> { order(created_at: :desc) }, :through => :categorizations
...
end
class Company < ActiveRecord::Base
belongs_to :admin, :class_name => :User, :foreign_key => "user_id"
has_many :company_users
has_many :users, :through => :company_users
has_many :categorizations, :as => :categorizeable
has_many :categories, -> { order(created_at: :desc) }, :through => :categorizations
...
end
class Category < ActiveRecord::Base
belongs_to :categorizeable, polymorphic: true
has_many :categorizations
has_many :companies, :through => :categorizations, :source => :categorized, :source_type => 'User'
has_many :contacts, :through => :categorizations, :source => :categorized, :source_type => 'Contact'
...
end
Related
Kind of a difficult concept to put into a title, but here is the use case:
I have users, the users can either belong to a partner or a customer. A partner can have many customers, but all users have a partner either directly or indirectly. Is there any way to create an association that would allow for partner access on any user regardless of whether or not they are directly related to it?
This is my current setup:
class User < ActiveRecord::Base
has_one :partner, through: :user_join, :source => :userable, :source_type => "Licensee"
has_one :customer, through: :user_join, :source => :userable, :source_type => "Customer"
end
class Partner < ActiveRecord::Base
has_many :customers
has_many :user_joins, as: :userable
has_many :users, through: :user_joins
end
class Customer < ActiveRecord::Base
belongs_to :partner
has_many :user_joins, as: :userable
has_many :users, through: :user_joins
end
This may be tough for me to explain, so if it's not clear just let me know so I can edit as needed!
I have the following example:
class User < ActiveRecord::Base
has_many :topics
has_many :memberships
end
class Topic < ActiveRecord::Base
belongs_to :user
end
#join model between User and Group
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
class Group < ActiveRecord::Base
has_many :memberships
has_many :members, :through => :memberships, :source => :user
has_many :topics, :through => :members
end
The problem I'm having is that I am trying to create a feed (#feed_topics) of all topics that are owned by all the members of the groups I am a member of, and I'm driving myself a little nuts.
Should I try to make this happen using associations, or make an instance method in my User model that has some ActiveRecord/SQL to union all the groups' members' topics into one ActiveRecord::Relation object?
My goal is to write current_user.feed_topics in my controller's action.
Sorry for not explaining earlier! The idea was to utilize 'Nested has_many_through's in order to get to your feed topics. This concept is documented here under the heading 'Nested Associations': http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html.
Let me know if this still is unclear (or if it doesn't work).
class User < ActiveRecord::Base
has_many :topics
has_many :memberships
has_many :groups, :through => :membership
has_many :group_members, :through => :groups, :source => :member
has_many :feed_topics, :through => :group_members, :source => :topic
end
So far these are the final versions of the models from the original question (topic and membership did not change):
class User < ActiveRecord::Base
has_many :topics
has_many :memberships
has_many :groups, :through => :memberships
has_many :feed_topics, :through => :groups, :source => :member_topics
end
class Group < ActiveRecord::Base
has_many :memberships
has_many :members, :through => :memberships, :source => :user
has_many :member_topics, :through => :members, :source => :topics
end
I am testing right now by adding more groups and members to see if it pulls in all the other members' topics of other groups.
EDIT: things seem to be working ok.
EDIT2: one little problem I had was seeing duplicate topics because a member was in multiple groups. I learned about :uniq => true and it saved the day.
I have a User model which has many projects and a Project model which can have many users, but also belongs to a single user (ie the user who created this project). It must belong to a User. It also allows a list of users to be associated with it, think collaboration.
With this in mind, my models look like this:
class User < ActiveRecord::Base
has_many :assigned_projects
has_many :projects, :through => :assigned_projects
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :assigned_projects
has_many :users, :through => :assigned_projects
end
class AssignedProject < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
Now, when I want to create a new project through a User, this is how I would do it:
user = User.create(:name => 'injekt')
user.projects.create(:name => 'project one')
Now, I know that projects is provided through an AssignedProject join model, which is why project.user will return nil. What I'm struggling to get my head around is the best way to assign the project creator (which by the way doesn't need to be user, it could be creator or something else descriptive, as long as it is of type User).
The idea then is to create a method to return projects_created from a User which will select only projects created by this user. Where user.projects will of course return ALL projects a user is associated with.
Assuming this kind of association is fairly common, what's the best way to achieve what I want? Any direction is greatly appreciated.
Add a creator_id column to your projects table for the creator relationship, and then add the associations to the models:
class User < ActiveRecord::Base
has_many :assigned_projects
has_many :projects, :through => :assigned_projects
has_many :created_projects, :class_name => "Project", :foreign_key => :creator_id
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :assigned_projects
has_many :users, :through => :assigned_projects
belongs_to :creator, :class_name => "User", :foreign_key => :creator_id
end
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many
I wanted to add little improvement to design. We don't actually need intermediate model because it does not contain any extra column other than reference_ids hence HABTM association is best suited over here.
class User < ActiveRecord::Base
has_and_belongs_to_many :projects, :join_table => :assigned_projects
has_many :created_projects, :class_name => "Project", :foreign_key => :creator_id
end
class Project < ActiveRecord::Base
has_and_belongs_to_many :users, :join_table => :assigned_projects
belongs_to :creator, :class_name => "User", :foreign_key => :creator_id
end
Can't wrap my head around this...
class User < ActiveRecord::Base
has_many :fantasies, :through => :fantasizings
has_many :fantasizings, :dependent => :destroy
end
class Fantasy < ActiveRecord::Base
has_many :users, :through => :fantasizings
has_many :fantasizings, :dependent => :destroy
end
class Fantasizing < ActiveRecord::Base
belongs_to :user
belongs_to :fantasy
end
... which works fine for my primary relationship, in that a User can have many Fantasies, and that a Fantasy can belong to many Users.
However, I need to add another relationship for liking (as in, a User "likes" a Fantasy rather than "has" it... think of Facebook and how you can "like" a wall-post, even though it doesn't "belong" to you... in fact, the Facebook example is almost exactly what I'm aiming for).
I gathered that I should make another association, but I'm kinda confused as to how I might use it, or if this is even the right approach. I started by adding the following:
class Fantasy < ActiveRecord::Base
...
has_many :users, :through => :approvals
has_many :approvals, :dependent => :destroy
end
class User < ActiveRecord::Base
...
has_many :fantasies, :through => :approvals
has_many :approvals, :dependent => :destroy
end
class Approval < ActiveRecord::Base
belongs_to :user
belongs_to :fantasy
end
... but how do I create the association through Approval rather than through Fantasizing?
If someone could set me straight on this, I'd be much obliged!
Keep your first set of code, then in your User Model add:
has_many :approved_fantasies, :through => :fantasizings, :source => :fantasy, :conditions => "fantasizings.is_approved = 1"
In your Fantasizing table, add an is_approved boolean field.
I have a schema where:
Students
has_and_belongs_to_many :courses
has_many :grades, :dependent => :destroy
has_many :assignments, :through => :grades
Courses
has_many :assignments, :dependent => :destroy
has_and_belongs_to_many :students
Assignments
belongs_to :course
has_many :grades, :dependent => :destroy
has_many :students, :through => :grades
Grades
belongs_to :student
belongs_to :assignment
I would like to add functionality whereby if a grade is added and the student does not belong to the course that the grade's assignment belongs to, then this relationship is made. Any suggestions as to the best way to do this? The grades_courses table does not have it's own model, will this need to be made?
A friend has suggested using after_create, but I don't know how to pass the parameters to this.
How about an observer on grades? Something like this
class GradeObserver < ActiveRecord::Observer
def after_create(grade)
unless grade.assignment.course.students.include?(grade.student)
grade.assignment.course.students << grade.student
end
end
end