Models, Associations, and more


In this guide, you will get a brief introduction to Rails models, which are wrappers around their database components. Models are the data that are represented by your app, and because they are purely data, they should not know anything about their representation in the views.

Rails utilizes relational models on top of a RDBMS (Relational DataBase Management System), which for the most part we will be using PostgreSQL. This means that within your application, certain models will be related to each other by different kinds of relationships (discussed more in the associations section). For example, in an app designed to serve online courses, the Student model may have many Courses, and Courses may have many Sections. All of these relationships will be to be specified by you, and the effect of achieving this is that you'll have a much more scalable application and more easily manageable resources.

Models

A model scaffold in Rails can be generated using the console. It is recommended to create models this way as it will also set up the proper migrations that you can immediately run afterwards, as well as test files that you can write later to verify your model behavior.

Let's generate a basic model called Course, that has a name and a description.

rails g model Course name:string description:string

This creates a migration that adds the courses table into your application schema (which you can find in schema.rb) and adds the appropriate name and description fields. Note that these fields are modified through migrations, and they are not usually not specified in your course.rb model file. An exception to this is when declaring another model to be an association, in which the association is specified, giving access to the associated model.

Here is an example of a Course model in a real Rails application :)

# == Schema Information
#
# Table name: courses
#
#  id           :integer          not null, primary key
#  name         :string           default("Course Name")
#  created_at   :datetime         not null
#  updated_at   :datetime         not null
#  description  :string           default("This is a course description.")
#  is_published :boolean          default(FALSE)
#

class Course < ActiveRecord::Base
  validates :name, :description, presence: true

  has_many :sections, -> { order(position: :asc) }
  has_many :code_courses
  has_many :codes, through: :code_courses

  has_many :course_requests
  has_many :requests, through: :course_requests

  has_one :photo, as: :parent, dependent: :destroy
  accepts_nested_attributes_for :photo

  scope :is_published, -> { where(:is_published => true) }

  def is_enrolled?(user)
    user.instance_of?(Admin) ||
    StudentCourse.find_by({ course_id: id, student_id: user.id})
  end

  def is_self_paced?(user)
    StudentCourse.find_by({ course_id: id, student_id: user.id, self_paced: true })
  end

  def current_subsection(user)
    sections.each do |section|
      section.subsections.each do |subsection|
        if !subsection.is_complete?(user)
          return subsection
        end
      end
    end

    sections.last.subsections.last
  end

  def progress(user)
    total_components = 0.0
    completed_components = 0.0

    sections.map(&:subsections)
            .flatten
            .map(&:components)
            .flatten.each do |component|
      total_components += 1

      completed_components += 1 if component.is_complete?(user)
    end

    puts completed_components
    puts total_components
    return total_components == 0 ?
      0 : (completed_components / total_components * 100).round
  end
end

Note that the associations to other models are delineated in the Course class via methods like has_many and has_one. Also, note that the model contains helper methods like current_subsection(user) to get the earliest subsection that the user has not already completed and progress(user) to get the percentage progress that a user has in this course. These methods rely only on the data and not their representation, so they are appropriate to include as part of your models if necessary.

Another thing to notice is that the first line within the class:

validates :name, :description, presence: true

is an example of a model validation checking the presence of certain attributes, which we will talk more about in the next section.

Validations

Validations are a way to ensure that the data you are putting into your application have a defined format and existence. For example, you would not want a user to be able to sign up for your application without a username or password, and you certainly wouldn't want your users to be creating accounts with invalid emails. The most basic type of validation is a validation on presence:

validates :name, presence: true

There are plenty of other things you can do with basic validations, such as checking uniqueness and inclusion:

validates :email, uniqueness: true
validates :activity, inclusion: {:in => Member.activities}

You can get even more fancy with validations, and use your own method to validate some property:

validate :correct_parent_password, on: :create

# This method checks that the user's inputted parent_password is
# correct before allowing the user to be created.
def correct_parent_password
  unless self.parent.valid_password?(parent_password)
    errors.add(:parent_password, 'for this parent is incorrect.')
  end
end

To learn more about Rails validations, visit this guide.

Associations Between Models

Associations are an essential part of understanding Rails models and building your applications to be scalable and manageable. There are six different types of associations that Rails supports, and you will commonly use three or four of them. Follow this great guide here, which goes into great detail about different associations and how to use them.

results matching ""

    No results matching ""