How should I architect this with Ruby on Rails? - ruby-on-rails

Sorry for the vague title, but this is kind of hard to put into one line.
I have a database full of contact information, and I want to be able to put those different contacts into groups which will also be stored in the database.
So, maybe I have 2 groups, "coworkers" and "neighbors", I want to be able to see a list of all my contacts and be able to add individual contacts to one or more groups.
I'm kind of confused as where to begin with this, could I get some basic outlines of how this might best be implemented? Thanks.

Well, you've got two models, Contact and Group. These two guys are clearly going to have their own tables (probably 'contacts' and 'groups' if you're following the Rails conventions). Since a contact can be in many groups, and a group can have many contacts, you've got what's called a many-to-many relationship between these models. There are basically two ways to implement this in Rails: has_and_belongs_to_many or has_many :through. Which one is best for you will depend a little on your situation.
has_and_belongs_to_many is probably the path of least resistance. You put a couple lines in your models like so:
# contact.rb
has_and_belongs_to_many :groups
# group.rb
has_and_belongs_to_many :contacts
...and create a table called 'contacts_groups' with the columns contact_id and group_id, and you're basically good to go. Easy peasy.
On the other hand, there are some advantages to using an association model with has_many :through. In this approach, you create another model, say, GroupMembership, and set up your models like so:
# contact.rb
has_many :group_memberships # this isn't strictly required, but I'd recommend it
has_many :groups, :through => :group_memberships
# group_membership.rb
has_many :groups
has_many :contacts
# group.rb
has_many :group_memberships # again, recommended but not required
has_many :contacts, :through => :group_memberships
This gives you most of the same convenience methods as has_and_belongs_to_many, and also lets you store any extra data you might want about the association, like the date they joined the group or the reason they were added. Even if you don't have any of that now, it's nice to account for the possibility of adding it later. Also, this lets you take a more RESTful approach to adding and removing contacts to and from groups, if that's something you're interested in, since you can model it in terms of creating and destroying GroupMembership resources.
By and large, I tend to lean toward the latter approach, especially as I've gotten more in to RESTful architectures. On the other hand, if you're not worried about REST and you're certain you'll never want to keep any extra information about memberships, has_and_belongs_to_many is probably simpler and requires less code to get working. For more in-depth information on the differences and implementation details, see the ActiveRecord Associations API docs.

You could structure a Ruby on Rails database to be :
Contact ( first_name:string, last_name:string, title:enum number:string, cell:string, notes:text, email:string ) => many_many :groups (or has_many :groups, :through=> :contact_group)
Contact_Group { group_id:integer, contact_id:integer }
Group ( name:string ) => many_many :contacts ) (or has_many :contacts, :through=> :contact_group)
That might be the general idea. You also could do relational fields.

You don't want to use the has_and_belongs_to_many approach. It's deprecated. Please read the API and make sure you implement a join model / has_many :through approach. The API will tell you how to do this and also mention why has_and_belongs_to_many is bad.
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Related

Relationship advice/association logic in rails

I'm designing an application to create goals for a classroom - at the moment, the data relationships are modeled like this
I knew that out of inexperience I might run into problems and now I'm beginning to - specifically in my routes file - but also just for generally organizing the site and the data relationships.
When a user signs up, they add a student_group (or class), populate it with students and then add subjects. Later they add goals for each subject - although there should also be goals for a student_group, a student, or even the user. I was thinking of something like this - but would it be better as a has_many, through relationship?
Right now, I've only really done work on the User, Student_group, and Student models and these are fairly straight-forward. A user has many student_groups, and a student_group has many students. I'd like a second opinion before I proceed however, so that I don't have to end up going back and doing things over. Thanks!
I think you might be thinking too far ahead. Once you have your app built around your current data model, you'll know better whether you even want to expand it to include the concept of a goal that isn't part of a student's subject. If you decide that it is, then making goals belong_to a subject, student, or user will be pretty simple. At that point, you could also do something like
Class Student
has_many :personal_goals, class_name: "Goal"
has_many :goals, through: :subjects
def all_goals
goals + personal_goals
end
There's probably a more elegant way to model that last relationship. Would you need to go beyond that? Does it make sense to talk about a student group having a goal of its own? I don't know.
As I gone through your database design, I have found that you should have to use different type of relationships, that rails has provided us. I tried my best to design your schema as per my knowledge. you should define relationship in your model as I suggested below. Any good modification are highly appreciated.
User
has_many :student_groups
has_many :students, through: :student_groups
has_many :goals, as: :goalable
StudentGroup
belongs_to :user
has_many :students
has_many :goals, as: :goalable
Student
belongs_to :student_group
has_many :subjects
has_many :characteristics
has_many :goals, as: :goalable
Characteristic
belongs_to :student
Goal
belongs_to :goalable, polymorphic => true
I have defined some polymorphic associations in your schema. If you need any reference related to these association. visit http://guides.rubyonrails.org/association_basics.html
Hope it will help you. Thanks.

Should I use `has_and_belongs_to_many` or `has_many :through` in my model?

I have read the Choosing Between has_many :through and has_and_belongs_to_many on the Rails website, however I am a bit confused since I have a different case to the ones given on the website.
I have two models: Prop and CharacterCostume, and the character's costume can have multiple props associated to it, but a prop doesn't belong to that character and it can be used by any number of characters in the scene, too.
Right now I have has_and_belongs_to_many :props inside my CharacterCostume model, which does exactly what I want it to do: it fetches all the props associated with the costume using a table named character_costumes_props when I call CharacterCostume#props
However the association name is putting me off because of the "belongs to many" part. The costume does not belong to any of the props, so there's no has_and_belongs_to_many :character_costumes inside the Prop model.
I know that it can all function fine without it, but it got me thinking that maybe I should use a has_many :through association, but that requires me to create a superfluous model (it is superfluous, right?) and the model would look like this:
class CharacterCostumeProp < ActiveRecord::Base
belongs_to :character_costume
has_one :prop
end
Also, would has_one instead of belongs_to work here?
I want the code to be as semantic as possible, but I am not sure if this will increase the requirement for resources or decrease performance in some way, since there's an intermediate model.
Are there certain quirks/benefits attached to either approach? Is mine good enough? Or is my thinking completely wrong from what I need to do?
Thanks!
I think you want to use a :has_many, :through because you're going to want to work directly with the relation model - what scene(s), consumed or damaged, etc.
But, the reason it reads funny to you is that, for the most part, has_many and belongs_to don't really mean what they mean in English. What they really mean is "They have the foreign keys" and "I have the foreign key", respectively; the exception being the :dependent => :destroy behavior.
That still doesn't really help with has_and_belongs_to_many, since you're then saying, "They have the foreign keys and I have the foreign keys` - except that you can think of it sort of adding a new part both to "I" and "They" that happens to be the same part for each, and has those keys.
Does that help?
The single most important question you must ask yourself when deciding between HABTM and has_many :through is this:
Do I want to store any information specific to the association?
Example 1: magazine subscriptions
A many-to-many relationship between readers and magazines might conceivably be structured as a HABTM or a has_many :through. However, the latter makes far more sense in this case because we can easily think of information specific to the association that we might want to store.
A reader is related to a magazine through a subscription, and every subscription can be described by fields such as price, starting date, issue frequency and whether it's active or not.
Example 2: tags
The relationship between an existing Tag model and, say, an Article model is clearly of the many-to-many kind. The fact that one particular tag has been associated to any particular article must have no influence on whether the same tag will be able to be similarly associated to other articles in the future.
But, differently from the previous example, here the association itself is all the information we need. We just need to know which tags are associated to any given article. It doesn't matter when the association was formed. It doesn't matter how long it lasted.
It may matter to us how many articles a tag is associated with. But that information is stored in the Tag model since it's not specific to an association. There is even a Rails feature that takes care of that called counter_cache.
has_one wouldn't work, you'd need belongs_to
it is not superfluous if you have logic in your association model
has_and_belongs_to_many is good enough for you
See example below
class Student
has_and_belongs_to_many :courses
has_many :teachers, through: :courses
end
class Teacher
has_many :courses
has_many :students, through: :courses
end
class Course
has_and_belongs_to_many :students
belongs_to :teacher
def boring?
teacher.name == 'Boris Boring'
end
end
In the example above, I make use of both versions. See how Course would have its own logic? See how a class for CourseStudent might not? That's what it all comes down to. Well, to me it is. I use has_many through for as long as I can't give a proper name to my association model and/or the model doesn't need extra logic or behavior.

Modelling an application in Rails - mixing STI and polymorphic associations

Please forgive me if this has been answered already but I've searched a lot and still require some clarification.
I'm building a Property Management tool using Rails 3.2. I already have a User model and authorisation/authentication code in place with full tests coverage.
I've just started drawing out the models/classes/tables and have gotten myself a bit confused.
Let's start with Users.
Modelling Users
I plan to have allow multiple companies to use this system. Each will have employees (users). These users will have different roles e.g. Manager, Agent, Accountant, Secretary etc. For the most part the data I plan to store for each of these users will be similar (or so I think at the moment) so I am leaning towards Single Table Inheritance and using the type to define the level of access each employee has.
Secondly, I plan to allow Landlord and Tenants to also log in to the system. Upon logging in they'll be able to view information about the property they are owning or renting - maybe keep their contact details up to date too.
I was thinking of using polymorphic associations to represent these users.
So the plan I have at the moment and would like some feedback on is to have
User < ActiveRecord::BASE
Employee < User (this will have a STI type column and allow for the different employee roles)
Landlord < User
Tenant < User
Is this the best way of approaching this problem or am I shooting myself in the foot?
I've had some people advise me I should have a 'roles' table and assign roles to the users - but I have a feeling this isn't the most elegant way to do this in Rails.
Properties
My next issue is with Properties. Right now I have a model for properties and when I add them they belong_to a User (i.e. they have a user_id foreign key). I then started thinking "what happens if the employee (user) that added the Property leaves the company or has their account deleted for some reason?"
So in this scenario is it best to forgo the User/Employee to Property association and just link the Property to the Company that the employee belongs to? This way I can all employee.company.properties to list out all the properties?
Landlord and Tenant associations
Let's presume we make Properties belong to a Company.
In terms of associations this is what I have in my head. Looking at it now I see that everything belongs to a company because one company using the system shouldn't be able to see the landlords/tenants/employees/properties of another company.
class Landlord < User
belongs_to :company
has_many :properties, :through => :ownerships
end
class Tenant < User
belongs_to :company
has_one :property, :through => tenancies #you can only live at one place at a time right?
end
class Property < ActiveRecord::Base
has_many :tenants, :through => :tenancies
has_many :landlords, :through => :ownerships
belongs_to :company
end
class Company < ActiveRecord::Base
has_many :properties
has_many :employees
has_many :landlords :through => :ownerships #not sure if through is required/works here
has_many :tenants :through => :tenancies #not sure if through is required/works here
end
class Employees < User
belongs_to :company
end
Properties
Also I'm guessing we'll have different types of Properties (Commercial/Residential) and of those there will be whole buildings, apartments within a building (single address) etc.
Like with users I'm planning on using Polymorphic Associations to define two subclasses CommercialProperty and ResidentialProperty and then use sti to define a type. If the type is "multi unit" then have a new model for units and an association whereby Property has_many Units and a Unit belongs_to a property.
Aim
I'm trying to make sure that my code follows best practice as much as possible so that when I come to add new features and expand I'm not left having to re-write large chunks of the app.
I would really appreciate your feedback.
Reference
Some of the posts I've read. Hopefully to help others trying to solve the same problem.
Designing a Rails application: single table inheritance?
Ruby on rails with different user types
Ruby On Rails User Model for multiple types
It's probably too late but you could also use has_and_belongs_to_many on User and Company and thus avoid STI altogether by using gems cancan and rolify.
It allows you to define finely grained access rights (abilities).
I know that it seems more elegant having different classes instead of roles, but it is not viable long-term strategy, it can become messy when logic becomes complex.
Other then that, the rest seems pretty solid, hope that helps :)

Correct implementation of many-to-many in Ruby on Rails?

Newbie question, beware! I'd like to implement a basic many-to-many relationship in Rails and I'm trying to find out which approach is considered the most "rails way" of them. In a traditional non-ActiveRecord DB I'd just created two tables and a junction table and written a bunch of logic to make sure all three tables are taken in consideration when operations are performed on any of them.
This is my first time using an ORM and so I'm trying to find out if perhaps ActiveRecord somehow simplifies the process for you, perhaps by not requiring a junction table to be manually created.
Railscasts seems like a reputable source of Rails wisdom, are the two ways in this cast truly "Rails way" or can I do better? - http://railscasts.com/episodes/47-two-many-to-many
There's basically two ways: has_and_belongs_to_many (habtm) and has_many with a :through option that points to another association. Both require join tables; the latter is what we call a join model, because you typically add more information to the join.
For example, consider an application with a User model who bookmarks Sites. One way would be to implement it as a habtm relationship
class User < ActiveRecord::Base
has_and_belongs_to_many :sites
end
class Site < ActiveRecord::Base
has_and_belongs_to_many :users
end
user.sites << Site.find(...)
This modeling will also require creating the sites_users table, which necessarily will lack a primary key.
The problem with this is you're likely to want to store additional information on it, so you might as well go with a join model, in this case Bookmark:
class User < ActiveRecord::Base
has_many :bookmarks
has_many :sites, :through => :bookmarks
end
class Site < ActiveRecord::Base
has_many :bookmarks
has_many :users, :through => :bookmarks
#edit: adding validation for requiring at least one bookmark
validate_before_create :at_least_one_bookmark
private
def at_least_one_bookmark
errors.add_to_base("requires at least one bookmark") unless bookmarks.count > 0
end
end
class Bookmark < ActiveRecord::Base
belongs_to :user
belongs_to :site
end
user.bookmarks.create(:site => Site.find(...) )
The more common pattern is the join model approach for its versatility and better modelling, though habtms are still used somewhat. They're just so two-dimensional that you really need to examine what you're doing and make sure there isn't some richer behavior that needs to be modelled as well.
Railscasts is the most reliable source of wisdom :) Rayan Bates teach a true "rails way" to solve problems in ruby on rails applications. You should definitely do it this way.
But if you want to learn more about associations then please follow this link http://guides.rubyonrails.org/association_basics.html
Happy coding!
As others have mentioned, Rails makes this all pretty easy using either has_and_belongs_to_many or has_many though.
I wouldn't say that one is "more right" than the other, it just depends on what you need to do, specifically around how you need to manipulate the joined models. Deciding between them is discussed here.
(All those references are from the association doc already provided.)

Rails has_one :through association

Rails has a has_one :through association that helps set up a one-to-one association with a third model by going through a second model. What is the real use of that besides making a shortcut association, that would otherwise be an extra step away.
Taking this example from the Rails guide:
class Supplier < ActiveRecord::Base
has_one :account
has_one :account_history, :through => :account
end
class Account < ActiveRecord::Base
belongs_to :supplier
has_one :account_history
end
class AccountHistory < ActiveRecord::Base
belongs_to :account
end
might allow us to do something like:
supplier.account_history
which would otherwise be reached as:
supplier.account.history
If it's only for simpler access then technically there could be a one-to-one association that connects a model with some nth model going through n-1 models for easier access. Is there anything else to it that I am missing besides the shortcut?
Logic, OK it might sound a bit weak for this but it would be logical to say that "I have a supplier who has an account with me, I want to see the entire account history of this supplier", so it makes sense for me to be able to access account history from supplier directly.
Efficiency, this for me is the main reason I would use :through, simply because this issues a join statement rather than calling supplier, and then account, and then account_history. noticed the number of database calls?
using :through, 1 call to get the supplier, 1 call to get account_history (rails automatically uses :join to retrieve through account)
using normal association, 1 call to get supplier, 1 call to get account, and 1 call to get account_history
That's what I think =) hope it helps!
I'm surprised no one has touched on Association Objects.
A has_many (or has_one) :through relationship facilitates the use of the association object pattern which is when you have two things related to each other, and that relation itself has attributes (ie a date when the association was made or when it expires).
This is considered by some to be a good alternative to the has_and_belongs_to_many ActiveRecord helper. The reasoning behind this is that it is very likely that you will need to change the nature of the association or add to it, and when you are a couple months into a project, this can be very painful if the relationship were initially set up as a has_and_belongs_to_many (the second link goes into some detail). If it is set up initially using a has_many :through relationship, then a couple months into the project it's easy to rename the join model or add attributes to it, making it easier for devs to respond to changing requirements. Plan for change.
Inverse association: consider the classic situation user-membership-group. If a user can be a member in many groups, then a group has many members or users, and a user has many groups. But if the user can only be a member in one group, the group still has many members: class User has_one :group, :through => :membership but class Group has_many :members, :through => memberships. The intermediate model membership is useful to keep track of the inverse relationship.
Expandability: a has_one :through relationship can easy be expanded and extended to a has_many :through relationship

Resources