Creating relationship between models. Rails 3 - ruby-on-rails

I have two model like so
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :remember_me, :fname, :lname
end
class Course < ActiveRecord::Base
belongs_to :university
attr_accessible :course_code, :department, :name, :path, :university_code, :department_code
set_primary_key :course_code
end
now a user can take many courses and a course could have many user. Also, the join table will have some attributes like the status of course user is taking, lectures he may have completed, etc.
How do I go about creating a relationship?

If a Course has a "uniqueness" notion, then you would need an intermediate model (Eg. UserCourseResults), so at any given state of your database, each Course can be unique. Then, you would just organize as follows:
User:
has_many :user_course_results
has_many :courses, through: user_course_results
Course:
has_many: :user_course_results
has_many: :users, through: user_course_results
UserCourseResult:
attr_accessible :result # So each user can have a result on each course, for instance.
belongs_to :user
belongs_to: :course
Do you see the point of the intermediate model?
HTH,
Pierre.
EDIT: i just saw your edit, and i guess this is exactly what you had in mind. You have to keep in mind that a User model only hold data that are unique per-user, the same notion goes for the Course model. So the best would probably be to have this intermediate "UserCourseResult" model, that would hold any user-course specific data...
This kind of relationship, when you don't need intermediate data in addition to the link between user and courses, is called HABTM (Has and belongs to many). You should have a look at this specific section in the active record doc (See has_many: through => * and has_and_belongs_to_many).

Related

Data modeling of Grandparent, Parent, and Child relationships in Rails

Is it a bad practice to set a model (table) association between both parent and child AND grandparent and child? For example, if I want to easily query a user's projects or a user's tasks, is the following setup recommended? If so, is there a good way to ensure both foreign keys always point to the same user?
Any help will be greatly appreciated.
class User < ActiveRecord::Base
attr_accessible :email, :first_name, :last_name
has_many :projects
has_many :tasks
end
class Project < ActiveRecord::Base
attr_accessible :name, :status
belongs_to :user
has_many :tasks
end
class Task < ActiveRecord::Base
attr_accessible :name, :status
belongs_to :user
belongs_to :project
end
There is nothing wrong with what you are trying to do, in fact rails has already predefined some association methods to help you. Just a couple of modifications and you are good to go
class User < ActiveRecord::Base
attr_accessible :email, :first_name, :last_name
has_many :projects
has_many :tasks, through: :projects
end
By adding the through: :projects to your tasks now means you can access all of a users tasks like so
$user = User.first #or whatever
$user.tasks
=> [AR array of all tasks]
You can play around with it in irb. In addition you don't need belongs_to :user in your Task model. It's already taken care of.
Look at section 2.4 for more details
EDIT: I have made the assumption (based upon your description) that a task belongs to a project, and a project belongs_to a user, and that user's don't have tasks directly, but through projects. If that was wrong, let me know and we'll figure it out from there.

rails - why polymorphic associations

An example from this blog
class Tag < ActiveRecord::Base
attr_accessible :name, :taggable_id, :taggable_type
belongs_to :taggable, :polymorphic => true
end
class Car < ActiveRecord::Base
attr_accessible :name
has_many :tags, :as => :taggable
end
class Bike < ActiveRecord::Base
attr_accessible :name
has_many :tags, :as => :taggable
end
It looks to me we could do thing like this without polymorphic associations
class Tag < ActiveRecord::Base
attr_accessible :name,
belongs_to :cars,
belongs_to :bikes,
end
class Car < ActiveRecord::Base
attr_accessible :name
has_many :tags
end
class Bike < ActiveRecord::Base
attr_accessible :name
has_many :tags
end
What is the difference of with polymorphic associations and without?
THanks
In your last example, Car has many tags, so conventionally the "tags" table will have a field car_id. Now Bike has many tags, so one more field bike_id.
Without Polymorphic, how many fields are you going to create for the tags table? :)
Even you guarantee that there will only be two models have tag ultimately, there will also lots of null data in the table, say a bike doesn't have car_id, and that is not nice.
Polymorphism solved this problem by defining a common interface so that Car and Bike can share same operations with different sub type. http://www.princeton.edu/~achaney/tmve/wiki100k/docs/Polymorphism_in_object-oriented_programming.html
A polymorphic association allows you to create a table with relationships between multiple models.
Consider your example. Both Car and Bike can have many tags, so instead of creating two different tables, say car_tags and bike_tags, you can use a single polymorphic table named Tag which not only stores the foreign key (in a column named resource_id), but also the type of resource it's associated with (in a column named resource_type), which, in this case would be Car or Bike.
In summary, polymorphic relationships are between many different models whereas normal relationships are, generally speaking, only between two.
You can find more information in the RoR Guides; http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Hope that helps clarify things a bit.
Polymorphic associations allow for a single model to belong to multiple models on a single association[1]. Taking your example above, simply adding has_many :tags to the Car and Bike models and belongs_to :car and belongs_to :bike to the Tag model has two major shortcomings:
It introduces a glut of foreign keys of which a large quantity will have no value
It still doesn't allow for a tag to belong to more than one model
Some great resources for learning more about polymorphic associations are listed below.
RailsGuides on Polymorphic
Associations
RailsCasts #154 Polymorphic Associations
(revised)
What's the Deal with Rails' Polymorphic
Associations?

ActiveRecord: Do I need both belongs_to and has_one

I have 2 models, namely user and userprofile. There is a one-to-one relationship between user and userprofile.
class Userprofile < ActiveRecord::Base
attr_accessible :fname, :lname, :iswoman, :age, :urlphoto, :user_id
belongs_to: user
end
class User < ActiveRecord::Base
attr_accessible :name, :provider, :uid
has_one: userprofile
end
I'd like to know whether I need both class to set the connection or having just either belongs_to or has_one is enough? The same is true for the other methods, such as has-many.
You define the association wherever you will need it. If at some point you need to say user.userprofile, then include has_one :userprofile in User. Likewise, if you need to say userprofile.user, then include belongs_to user in Userprofile.
In other words, associations are relative. You can specify that model A has_one :b without specifying that model B belongs_to :a. You simply define what you need. The same goes for one-to-many and many-to-many associations.
Just be sure to have migrated user_id to the "userprofiles" table.
Having just a belongs_to relationship between userprofiles and user does default to has_one. However, it would be wise (Rails-proper) to specify the association on both models.
After all, if you wanted a has_many association (etc) you would want to specify that.
Check out http://guides.rubyonrails.org/association_basics.html for more info

ruby on rails model

I have two models as subject and teacher
Subject model as
class Subject < ActiveRecord::Base
belongs_to :sclass
has_many :subject_teachers
attr_accessible :sub_name
end
and Teacher model as
class Teacher < ActiveRecord::Base
# attr_accessible :title, :body
has_many :sclass_teachers
has_many :subject_teachers
attr_accessible :fname, :lname, :mob, :email
end
and created their join table as subject_teacher as many to many relationship
class SubjectTeacher < ActiveRecord::Base
belongs_to :subject
belongs_to :teacher
end
but i want to access teacher name in subject model / table how can i do it.
what and where did i write the perfect code so that i get specific teacher name to specific
subject as there are MANY TO MANY relationship?
You would do:
has_many :teachers through => :subject_teachers
as #Hugo said
You can access the teachers data from subject using the has_many :through relationship
http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
What you say is a bit strange: you set up a many to many relationship, and require a specific teacher for a subject. So for a many to many relationship, you can get a list of teachers. And from that list you could take the first one. But if you know there is at max one teacher for a subject, just a has_many is a lot more convenient.
You can access the teachers by using #subject.subject_teachers.teachers, where #subject is a Subject instance.
If you use a has-many-and-belongs-to-many relationship, you can omit the intermediate class. You only need that is the relationship has additional data, like a date and time.

has_one and has_many in same model. How does rails track them?

I a little confused about how this work even if it works properly. I have a model that has two association to the same other model.
Company has an owner and company has many employees of the class users.
here is my company model:
class Company < ActiveRecord::Base
validates_presence_of :name
has_many :employee, :class_name => 'User'
has_one :owner, :class_name => 'User'
accepts_nested_attributes_for :owner, :allow_destroy => true
end
here is my user model:
class User < ActiveRecord::Base
include Clearance::User
attr_accessible :lastname, :firstname #other attr are whitelisted in clearance gem
validates_presence_of :lastname, :firstname
belongs_to :company
end
Now assuming I have 3 employee of this company including the owner. When I first create the company I set the owner to the employee with id 1 and the two others (2,3) are added to the employee list by setting their company_id (user.company=company). All three have their company_id set to the company id which we can assume is 1
when I ask for company.owner, I get the right user and when I do company.employee, I get all three.
If I change the owner to user 2, it removes user 1 from the employees automatically by setting it's company_id to nil. This is fine and if I add him back as a simple employee all is still good.
How the heck does rails know which is which? What I mean is how does it know that an employee is owner and not just an employee? Nothing in the schema defines this.
I have a feeling I should reverse the owner association and make company belong_to a user.
As you have it now, there's nothing to distinguish owners from employees. Which means you're going to run into problems once you start removing people or try to change ownership.
As François points out, you're just lucking out in that the owner is the user that belongs to company with the lowest ID.
To fix the problem I would have my models relate in the following maner.
class Company < ActiveRecord::Base
belongs_to :owner, :class_name => "user"
has_many :employees, :class_name => "user"
validates_presence_of :name
accepts_nested_attributes_for :owner, :allow_destroy => true
end
class User < ActiveRecord::Base
include Clearance::User
attr_accessible :lastname, :firstname #other attr are whitelisted in clearance gem
validates_presence_of :lastname, :firstname
belongs_to :company
has_one :company, :foreign_key => :owner_id
end
You'll have to add another column called owner_id to the Companies table, but this more clearly defines your relationships. And will avoid any troubles associated with changing the owner. Take note that there might be a cyclical dependency if you go this route and have your database set so that both users.company_id and companies.owner_id cannot be null.
I'm not quite sure how well accepts_nested_attributes_for will play with a belongs_to relationship.
has_one is syntactic sugar for:
has_many :whatevers, :limit => 1
has one adds the :limit => 1 bit, thereby ensuring only 1 record is ever returned. In your has one declaration, make sure you have an :order clause, to return the right record in all circumstances. In this instance, I'd put a flag on the Employee to signify who is the owner, and sort by this column to get the right record 1st.
Your question about how come Rails knows this is because most databases will return records in their primary key order. So, the 1st added employee has ID 1, thereby will be returned 1st.
You could have a model called ownership -
ownership belongs_to company
ownership belongs_to user
user has_many ownerships
company has_one ownership

Resources