A maze of Associations

25 Aug 2017

When I started learning Rails, I was extremely confused in associations. They are like a maze in which getting lost is easy.

Associations serve as link between models. They make performing operations on the records easier.

So, let’s take plunge into the details of Associations!

There are four types of associations.

  1. One-to-Many
  2. One-to-one
  3. Many-to-Many
  4. Polymorphic One-to-Many


  1. One-to-Many : One-to-Many is one of the most commonly used association. It states that an instance of model has many instances of another model. For example, A student is enrolled in a University but a University has many students.
    We can say :
    A Student belongs_to a University.
    A University has_many Students.

      class Student < ApplicationRecord
        belongs_to :university
      end
    
      class University < ApplicationRecord
         has_many :students
      end
    


    belongs_to is usually on the other side of has_many or has_one relationship.

    Once we establish One-to-Many association, we get some very useful methods at our disposal:

    • university.students : gives us all the students, enrolled in a university.
    • student.university : gives us the name of university student is enrolled in.
    • university.students << student1 : adds student1 to the collection and returns the collection.
    • university.students.delete(@student1) : deletes student1 from the collection. Sets foreign key to null.
    • university.students.destroy(@student1) : removes @student1 from collection by running destroy on each object.
    • university.students.build({ }) : instantiates a new student but does not save it.
    • university.students.create({ }) : validates and creates a new student and saves it in the database.
    • university.students.create!({}) : validates, creates and saves student in database, but returns the exception in case of failure.
    • university.build_student : instantiates a new student but does not save it.
    • university.create_student : creates a new student and saves it in the database.


  2. One-to-One : One-to-One association states that one instance of model contains exactly one instance of other model. What that means is, for example: A student has one University Account. One University Account belongs to one Student
    We can say:
    A student has_one Account
    An Account belongs_to a Student

     class Student < ApplicationRecord
       has_one :account
     end
    
     class Account < ApplicationRecord
       belongs_to :student
     end
    


    After the relation is established, in One-to-One as well we get few useful methods:

    • student.account : gets the account details for the student.
    • student.build_account : instantiates the new account but does not save it.
    • student.create_adress : validates, creates and saves new address to the database.


  3. Many-to-Many : This association says that many instances of model has many instances of another model. It needs a join table which stores the relation between the two models.

    It can be set up in two ways :
    a) Has_And_Belongs_To_Many (HABTM): In case of Has And Belongs To Many , lets take an example and say, Teachers teach many Students and Students are taught by many Teachers. We can say it as :
    Teacher has_and_belongs_to_many Students
    Student has_and_belongs_to_many Teachers

    In this case the join table name by default will be students_teachers, but can be customized by the programmer.

     class Teacher < ApplicationRecord
       has_and_belongs_to_many :students
     end
    
     class Student < ApplicationRecord
       has_and_belongs_to_many :teachers
     end
    


    Now, with the establishment of HABTM relation, we get another set of methods to play around with:

    • student.teachers: all teachers teaching a particular student
    • student.teacher_ids: returns ids from the colection
    • student.teacher_ids = [1, 2, 3] : creates collection with objects corresponding to PK values supplied.
    • student.teachers.delete(teacher_1) or student.teachers.destroy(teacher_1): deletes or destroys the relations for teacher_1
    • teachers.empty? : checks if teachers contains data
    • teachers.size : checks the size of teachers collection
    • student.teachers.create(attributes = {}): creates and adds objects to the collection.


    HABTM is an approach which looks easy, but it is very rigid in the sense that we gain almost no flexibility of working with the relationship model. This problem is solved using Has Many Through relation.

    b) Has Many Through : Has Many Through is a more flexible approach under many_to_many relation. Let’s say that Students in a university are to be sent many reminders for events and each event has many students attending it.
    We can say:
    Students has_many Reminders through StudentsReminder
    Reminders has_many Students through StudentsReminder
    StudentsReminder belongs_to Students and Reminders

        class Student < ApplicationRecord
          has_many :students_reminders
          has_many :reminders, through: :students_reminders
        end
    
        class StudentsReminder < ApplicationRecord
          belongs_to :students
          belongs_to :reminders
        end
    
        class Reminder < ApplicationRecord
          has_many :students_reminders
          has_many :students, through: :students_reminders
        end
    


    Has Many Through is a more flexible approach because now we can work with StudentsReminder model as well.

  4. Polymorphic One-to-Many: In Polymorphic association, the model belongs to more than one other models, on a single association. For example, a photo model can belong to either Student or Teacher.

     class Photo < ApplicationRecord
       belongs_to :identification, polymorphic: true
     end
    
     class Student < ApplicationRecord
       has_many :photos, as: :identification
     end
    
     class Teacher < ApplicationRecord
       has_many :photos, as: :identification
     end
    


    While setting up Polymorphic Association we must declare Foregin Key column and a Type column in the Photos model.

    Now, that we are done with setting up , we can go ahead and access the photos belonging to students and teachers.
    From an instance of Student we can say, @student.photos.
    And, from an instance of Teacher model, @teacher.photos.

    How amazing is that!