Best way to model these Rails relationships - ruby-on-rails

I have the following scenario that I want to model the best way for a Rails application.
I have a contact model that belongs to a company. A contact can create an enquiry so contact can have many enquiries. The enquiry can have many messages that can be from and to many contacts. Messages can belong to an enquiry, a company or a contact.
This has been confusing me on how best to model this. Any ideas?
Best regards,
Richard Moss

It seems that Message should be polymorphic in this case, since they can belong to many different models. Because contacts can send messages to other contacts, Contact will have two associations with messages, one for sent_messages and the other for received_messages. Message will be tied to a sender through contact_id.
class Contact < ActiveRecord::Base
belongs_to :company
has_many :enquiries
has_many :sent_messages, :class_name => "Message"
has_many :received_messages, :as => :messageable, :class_name => "Message"
end
class Company < ActiveRecord::Base
has_many :contacts
has_many :messages, :as => :messageable
end
class Enquiry < ActiveRecord::Base
belongs_to :contact
has_many :messages, :as => :messageable
end
class Message < ActiveRecord::Base
belongs_to :contact
belongs_to :messageable, :polymorphic => true
end
This should model your relationship requirements pretty well. If you want to better understand it, the Railscast on Polymorphic Associations might be of some insight as well.

It sounds like the confusion is on the messages, rather than the other parts.
I'd go with belongs_to for each of enquiry, company and contact (I'm assuming that you have some messages not associated with enquiries). Then I'd do a custom validation to make sure that at least one of those was specified - but otherwise don't do a validates_presence_of for any of them.
A helper method could ease the pain of finding what a given message is associated with, so you don't have to check each of those three relations looking for one that is not nil.

Related

Related model can be one of two other models

I currently have essentially 3 models at the moment. I have Project, User and Contact.
I'm trying to assign users or contacts as a sort of 'member' to each project. I initially thought that a linking table here would suffice, for example ProjectMembers but i'm currently hitting a brick wall in my thought process when it comes to a project member only being allowed to be either a Contact or a User and whether to handle this via a relationship or by code in the model or the controller by checking which between user_id or contact_id was not null.
I had a look at polymorphic associations which looked promising, but somehow I ended up with the association backwards (Projects were being entered into the ProjectMember table as the type, rather than User or Contact) and confused myself even more.
The final output I would pretty much like would be simply to have the ability to run something like Project.first.project_members and have those members return with the role in that project. It'd be even nicer if I could run User.first.projects/Contact.first.projects and get those too, but that's something I can figure out down the line.
Any pointers would be greatly appreciated.
Polymorphic association is something that you should go for here.
First: Project has a many to many relationship with User and Contact, hence you need a join table.
Second: Since you need to call something like Project.first.project_members, you should have a polymorphic association.
For this scenario, you should create a polymorphic join table.
Let's say you have the polymorphic table ProjectMember with memberable_id, memberable_type and project_id (well I have a hang of railcasts).
class ProjectMember < ActiveRecord::Base
belongs_to :memberable, :polymorphic => true
belongs_to :project
end
and then in your User model,
class User < ActiveRecord::Base
has_many :project_members, :as => :memberable
has_many :projects, through: :project_members
end
and in Contact model
class Member < ActiveRecord::Base
has_many :project_members, :as => :memberable
has_many :projects, through: :project_members
end
and in your Project model
class Project < ActiveRecord::Base
has_many :users, :through => :project_members, :source => :memberable, :source_type => 'User'
has_many :contacts, :through => :project_members, :source => :memberable, :source_type => 'Contact'
has_many :project_members
end
Now you can call your desired associations, Project.first.project_members, User.first.projects or Contact.first.projects. Hope this helps.
Thanks

Model association between Company, Employee, and department

I am working in Ruby on Rails 3. And trying to map out three models which mimic the data of a Company its employees and their respective departments.
In arrived at the following solution:
class Company < ActiveRecord::Base
has_many :departments
has_many :employees, through => :departments
end
class Department < ActiveRecord::Base
belongs_to :company
has_many :employees
has_one :department_description
end
class DepartmentDescription < ActiveRecord::Base
belongs_to :department
end
class Employee < ActiveRecord::Base
belongs_to :department
end
Is this the 'correct' way to associate these models?
I think your last response may explain why you are struggling to find a correct way to associate these models.
It seems that you see your Department merely as a join_table, and that may be due to the fact that you don't fully understand the has_many => :through construction and that it actually allows your Department to be a proper model with many attributes and methods in it, hence also a 'description' attribute.
To create a separate DepartmentDescription model is actually a waste of resource. Chad Fowler has a few good examples for :has_many => through and nested resources in his Rails Recipes... so check that out.

Rails model advice

I need some suggests for the model relationship that I'm going to develop:
I want to create a research table that keep track of all the reasearches performed inside the platform.
In the platform I'm developing, an user can search for other users.
In order to do it, I thought to create 3 fields in the research table: Performer (user_id that submit the research) Research_string (The string that the performer submit) and Results (that is one or more user).
Of course the relationship among user and research tables will be many to many, but note that the research table has 2 different column that involves the user_id (performer and results) so how can I specify to rails ? I thought something like that :
class User < ActiveRecord::Base
has_many :performed_research, :through => :research_table
class Research < ActiveRecord::Base
has_many :users
But how can I specify that the has_many users in the Research tables implies two different relations depending by the column ?
Tnx
EDITED: my solution
Your solution is not correct, because the user has only the research performed and not the research where he is resulted. I made another solution, that it's not the best of clear but it works, I would like to have your judge:
class Research < ActiveRecord::Base
belongs_to :searcher, :class_name => 'User', :foreign_key=> 'submitter_id'
has_many :found_users, :through=>:user_researches,:source=>:user
#It is necessary to let the association work without class_name it would look for
userResearch class
has_many :user_researches, :class_name =>'User_research'
end
class User_research < ActiveRecord::Base
belongs_to :user
belongs_to :research
end
class User < ActiveRecord::Base
# It returns a list of research performed by the user
has_many :researches, :foreign_key => 'submitter_id'
# It is necessary to let the searcher relationship works
has_many :user_researches, :class_name =>'User_research'
#==> Searcher will return an Array of research where the user's skill has been researched
has_many :follower_researches, :through => :user_researches, :source=>:research
end
I say that it;s not the best because the follower_research of the user model, show an array of research when he has been results ... and not an Array of submitter that searching him, so to obtain them, I have to scroll the array of research and then take the searcher field ... Are you able to perform an improvement (hopefully less complex than this)
Do you mean something like this?
class User < ActiveRecord::Base
has_many :research_entries
class ResearchEntry < ActiveRecord::Base
has_one :performer, :class_name => 'User'
has_and_belongs_to_many :resulting_users, :class_name => 'User'
Update: I changed this to use the has_and_belongs_to_many relationship which will allow you to use a join table to connect many users into "resulting_users" field of the ResearchEntry
Something like this:
class User < ActiveRecord::Base
has_many :submitted_researches, :class_name => 'Research', :foreign_key => 'submitter_id'
has_manu :researches, :through => :performed_researches
class Research < ActiveRecord::Base
belongs_to :submitter, :class_name => 'User'
has_many :researchers, :through => :performed_researches, :class_name => 'User'
class PerformedResearch < ActiveRecord::Base
belongs_to :user
belongs_to :research
See: http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
Update: I think I misread something. What is results? and how is it related to users?
Update 2: Still not sure I understand but here's another stab
I originally thought you were doing something related to academic research and the Research model was for research papers with the performer being scientists that did the research.
From re-reading your question I'm now thinking you have a rails application with some users and a search feature that lets a user search for other users and for each search you are trying to keep track of which user did the search and which users they found.
Let me know if any of these assumptions are wrong.
Based on that:
class User < ActiveRecord::Base
has_many :searches #all the searches this user performed
class Search < ActiveRecord::Base
belongs_to :searcher, :class_name => 'User' # the user who performed the search
has_many :found_users, :though => :user_search_results, :foreign_key => 'user_id' # using foreign_key because I want to change the name to 'found_users'
#alternatively if you don't want to use foreign_key use the line bellow instead
#has_many :users, :thorough => :user_search_results
class UserSearchResult < ActiveRecord::Base
belongs_to :user
belongs_to :search

Setting a type for one side of a many-to-many association with a join model

I set up a public github app (see: https://github.com/greenplastik/testapp to download) to work through a problem I'm having with specifying a type on one side of a many-to-many association between two models, via a join model.
Given Person and Book models and a Book_Person join model, I want to be able to do the following:
#book = Book.first
#book.people # lists people for book
#book.authors # lists author-type people for book
#book.editors # lists editor-type people for book
and
#person = Person.first
#person.books # lists books for people
This app was set up in part using the instructions found through Google. There's a link to those instructions in the README of my testapp.
I tried, as best I could, to remove the inconsistencies and typos. I can't get it to work.
Any help would be appreciated. I've included the sqlite database for easier testing on your end.
I'm sorry i haven't got time to post neither a full solution or a tested one, but this should at least put you on the right track..
The best way to achieve what you're looking for is with Single Table Inheritance used with a polymorphic relationship.
I'd suggest redefining Author and Editor to be subclasses of Person
As in
class Person < ActiveRecord::Base
has_many :book_people
has_many :books, :through => :book_people
end
class Author < Person
end
class Editor < Person
end
class BookPerson < ActiveRecord::Base
belongs_to :person, :polymorphic => true
belongs_to :book
end
class Book < ActiveRecord::Base
has_many :book_people
has_many :people, :through => :book_people
has_many :authors, :through => :book_people, :source => :person, :source_type => "Author"
has_many :editors, :through => :book_people, :source => :person, :source_type => "Editor"
end
However this would be suboptimal if a single person could fill all three roles. There's probably a way around that by using the same STI name for the three of them. But then you'd make it harder to query for all authors.

Rails modeling for a user

When building a rails app that allows a User to login and create data, is it best to setup a belongs_to :user association on every single model? For example, let's say a user can create Favorites, Colors and Tags.
And let's say Favorites has_many :tags and Colors also has_many :tags. Is it still important for Tags to belong_to :user assuming the User is the only person who has authority to edit those tags?
And a similar question along the same lines: When updating data in FavoritesController, I've come to the conclusion that you perform CRUD operations by always doing something like current_user.favorites.find(param[:id].update_attributes(param[:favorite]) so that they can definitely only update models that belong to them. Right?
Update Wasn't too happy with any of the answers, as no one really answered my question but instead went after the for-example-only Tags model suggesting better ways to do that. I'm assuming I was right, and models should belong_to :user. I also discovered some great security tips that address my questions here: http://asciicasts.com/episodes/178-seven-security-tips
As you describe the tags it seems that they are more of an aspect, so you can implement them as a polymorphic association. But you should do it many-to-many, as tags can be reused among users and taggable objects. Let's call the join model Tagging, which will be the one that belongs to user if you want to remember who created the tagging.
class Tag < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :colors, :through => :taggings, :source => :taggable, :source_type => "Color"
has_many :favorites, :through => :taggings, :source => :taggable, :source_type => "Favorite"
end
class Tagging < ActiveRecord::Base
belongs_to :user
belongs_to :taggable, :polymorphic => true
belongs_to :tag
end
class Color < ActiveRecord::Base
belongs_to :user
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
end
class Favorite < ActiveRecord::Base
belongs_to :user
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
end
class User < ActiveRecord::Base
has_many :favorites
has_many :colors
has_many :taggings
has_many :tags, :through => :taggings
end
As for the Favorite updating, I agree with you: you will mostly work within the scope of a user (most likely the currently logged in user).
It depends on your model. Both cases are valid but I'd discorage making a circular relationships like that. Having a hierarchy is more flexible. For example: User->Favorites->Tags (unless you want to tag users as well)
User.favorites.find(params[:id]).update_attributes(param[:favorite])
is what you mean I guess (syntax). Whoever calls the URL will perform that action. Dont rely on the fact that that URL is visible to one user only (owner of the favorite). You should have checks in place that the currently logged in user is the only one performing actions on the objects that belong to him.
The proposed mechanism sounds a bit too complex for me. I prefer the current_user way. Assume there is a current_user (following the authlogic way) in your authentication system, then simple add a user references (user_id) in every relevant table. Update the current_user for new or update record via a controller filter.
In the models, put relevant belongs_to :users accordingly, put enough has_many in users model if needed.
:has_many and :belongs_to in AR will explains the relationship between models, but not necessarily you have to use them in your models, the associaton between them will be already present in the tables as a foreign key.
But adding :has_many or :belongs_to to your models will give you extra methods to your model
ex:
class User < ActiveRecord::Base
has_many :favorites
#def favorites
# Favorite.find_all_by_user_id(self.id)
# end
end
If you mention has_many it will give a new method in your model called favorites, that method will be invisible (will be present in the AR).
Similarly for any association, if you are planning to use this kind of methods you should use associations in your models.

Resources