CSC/ECE 517 Fall 2012/ch1b 1w63 dv

From Expertiza_Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Active Records

Introduction

The Active Record pattern is a Design pattern in Software Engineering which deals with the approach to store and access data in a database. The interface of an object conforming to this pattern would contain functions to perform operations like Insert, Read, Update, and Delete. The Object will have properties that correspond to the columns in the underlying database table. This pattern is realized through ORM (Object-Relational Mapping) libraries in Programming languages.

ActiveRecord is a module for Ruby that can be used for ORM. Thus, it is obvious that ActiveRecord will form a part of the Model in an MVC application developed in Ruby. The rest of the article discusses ActiveRecord that is the Ruby module for implementing the Active Record pattern.

The ActiveRecord module insulates the developer from the need to use SQL in most cases. Internally, It will perform queries on the database which corresponds to the method invoked on the object. This module is compatible with most database systems (most used ones like MySQL, PostgreSQL and SQLite). Moreover, regardless of which database system the developer uses, the Active Record method format always remains the same.

Naming

The ActiveRecord module uses a convention for naming classes, tables and fields so that the amount of configuration needed to get the functionality working is minimal. There are naming conventions on files, classes, tables etc.

Reserved names and Attributes

Certain names are reserved and should not be used (even in the model as attributes). Some of them are listed below:

  • lock_version.
  • type - This is only used when you have single table inheritance and must contain a class name.
  • id - Reserved for primary keys.
  • table_name_count - Reserved for counter cache.
  • position - Reserved for acts_as_list.
  • parent_id - Reserved for acts_as_tree.
  • lft - Reserved for acts_as_nested_set.
  • rgt - Reserved for acts_as_nested_set.
  • quote - Method in ActiveRecord::Base which is used to quote SQL.
  • template.

Class Naming

The Classes inheriting from ActiveRecord are named in singular form. e.g User

Table Naming

Tables for ActiveRecord objects are named in plural form by default. e.g. Users This naming convention can be circumvented by using below:

a) Set use_pluralization In the environment.rb file we can specify

  ActiveRecord::Base.use_pluralization = false. 

This will disable pluralization for all ActiveRecord objects.

b.) Use set_table_name You can call set_table_name to specify a custom table name for a particular model. For example:

 class User < ActiveRecord::Base
   set_table_name 'user'
 end

CRUD

CRUD stands for 'Create', 'Read' , 'Update' and 'Delete'. These are the four basic operations which are generally performed on tables in a database. The ActiveRecord module provides predefined methods for the basic CRUD operations for the model.

Create

A new record can be created in the table by invoking the “save” function on the model object whose record has to be created in the database. ActiveRecord will use the Object's attributes as the field values for the record. The data is not persisted in the database until we call the save function.

  @user = User.new
  @user.name = “abc”
  @user.age = 23
  @user.save      #returns a boolean indicating whether the save was successful or not (whether a new record was created or not)


ActiveRecord provides another convinient way to create a new record without creating instantiating the model explicitly and then using “save”. To do this, we use the 'create' function. By default the primary key used in the table is “id” which is generated automatically.

  User.create(:name=>”xyz”, :age=”23”)

Read

A record can be read from the table by using the various functions like “find” (find the model record by specifying a value used in its primary key), “where”, “all” , “first” and “last”. All these functions instantiate a new Object for the model and populate its attributes using the fields of the record.

  @user_first = User.first #Finds and returns the 1st User from the table
  @user_last = User.last #Finds and returns the Last User from the table
  @all_users = User.all # Returns all the Users from the table
  @my_user = User.find(5) #Finds and returns the record from the users table whose id = 5
  @my_other_user = User.where(:name=>”abc”) #Finds and returns the user whose “name” is “abc” .

Dynamic Finders

Some of the most common searchs performed on databases are to return the rows where a column matches a given value. In many other languages and frameworks, we would generally need to construct SQL queries to perform these searches. ActiveRecord uses Ruby’s dynamic power to do this for us.

For example, our User model has attributes such as name and age. We can use these names in finder methods to return rows where the corresponding columns match some value:

  @my_user = User.find_by_name(“abc”)
  @my_user = User.find_by_age(15)

Update

A record in the table corresponding to a given model instance can be Updated by using the function “save”.

  @my_user = User.find(5) #Finds and returns the record from the users table whose id = 5
  @my_user.name = “test”
  @my_user.save

Moreover, we can combine the functions of reading a row and updating it using the class methods update and update_all. The update method takes an id parameter and a set of attributes. It fetches the corresponding row, updates the given attributes, saves the result to the database, and returns the model object.

  @my_user = User.update(1, :name=”test3”)
  @result = User.update_all(“age= age+1”)


Delete

A record can be deleted from the table by invoking the “destroy” functionality on the instance of the object. The destroy instance method deletes from the database the row corresponding to a particular model object. It then freezes the contents of that object, preventing future changes to the attributes.

  @my_user = User.find(5) #Finds and returns the record from the users table whose id = 5
  @my_user.destroy # deletes the record corresponding to the user with id = 5 from the table
  # ... my_user is now frozen


It also has two class-level methods, delete and delete_all. The delete method takes a single id or an array of ids and deletes the corresponding row(s) in the underlying table. delete_all deletes rows matching a given condition.

  User.delete(1)
  User.delete([2,3,4,5])
  User.delete_all(["age < ?" , 18])

The “delete” methods bypass the ActiveRecord callback and validation functions that may be defined for the model class, while the “destroy” methods ensure that they are all invoked. Hence, it is better to use the “destroy” methods as it ensures that our database is as per the business rules defined in the model.

Connecting to the Database

The ActiveRecord connection adapter is meant to wrap and abstract away the underlying driver specific to database, and is meant to provide an interface which is common for database tasks such as creating and destroying databases, modifying tables, updating, deleting, and inserting data, managing transactions and running queries . The connection adapter is normally used internally by ActiveRecord but can be used without the help of ActiveRecord models as well.

A connection adapter can be obtained in the following manner:

 connection = Category.connection
 object = Category.find(1)
 connection = object.connection

Most applications connect to only one database which is defined in the database.yml file. In such a scenario every class which inherits from ActiveRecord::Base will be using the same connection. But in some special cases the application may also connect to a secondary database. That is the case in which some ActiveRecord classes connect to a secondary database. In such cases extra care need to take so that every class asks for a connection from the right database.

Rails generally opens several connections at once, and these connections are managed in a pool. Each connection adapter object forms a single connection to some database. Connections can run only one SQL statement at a time, so generally one connection is opened per thread. When a job needs a connection to database, it checks out one of the pool which is returned when it finishes for use by another task.

Running Low-Level Queries

ActiveRecord “model” objects are returned by most calls in the standard ActiveRecord API. There might be cases in which you want to bypass the overhead involved in creating full ActiveRecord objects, or maybe want to query data that does not have a corresponding ActiveRecord class. SQL queries can be written using the connection adapter's low-level query methods.

In this first example, we get the “category_name” value from a single row in our “categoriess” table. If we only need the category name, we can grab the connection object and use the select_value method as shown below:

 connection = Category.connection
 category_name = connection.select_value("SELECT name FROM categories WHERE id=1")
 # => "Football"

Migrations

Migrations help to version the various changes made to databases. It also allow developers to track a set of changes made to production or development databases and to rollback to a previous version if needed.

Building a Migration

You can either build the migration on its own using

ruby script/generate migration User

Specific commands can be written afterwards to create custom SQL. A model can also be created that comes with the migration.

ruby script/generate model User name:string user_id:integer

The migration will generate a couple of new files under the "db" directory. The contents of such a generate file are as follows:

 # 9889904091223123_create_user.rb
 class CreateUsers < ActiveRecord::Migration
   def self.up
     create_table :users do |t|
       t.string :name
       t.integer :user_id
       t.timestamps
     end
   end
   def self.down
     drop_table :users
   end
 end

The "9889904091223123" at the beginning of the file-name is the timestamp. The timestamps will be different depending on the time of creation or modification of the database. This is helpful to rollback to a previous state if needed. This way the developer need not remember how the current state is reached and how to go back to a previous state.

The self.up from the previous code snippet creates the User table and add the columns. The self.down method is used to drop the table and to remove all the contents. The self.up and self.down methods are necessary to keep the database consistent after a rollback.

Rails adds an additional column called the "timestamps" to keep track of when each row was added. Rails also creates a primary key of the form "model_name"_id which increments automatically every time a row is added.

Different types of datatypes can be used with ActiveRecord. Some of the most commonly used ones are:

  • integer
  • string
  • text
  • boolean
  • references
  • decimal
  • timestamp

The migration file can be written into the database using the following command:

rake db:migrate

The above command will create the table and the various columns. This command can be used to migrate many files in one go. Connections between tables in the database can be introduced using references in the model.

Associations

Associations are used to connect two models. The association is used to describe the role of relations that models are having with each other. ActiveRecord associations can be used to describe one-to-one (1:1), one-to-many (1:n) and many-to-many (n:m) relationships between models. Associations are used to make common operations simpler and easier in your code. Rails supports six types of associations:

  • belongs_to
  • has_one
  • has_many
  • has_many :through
  • has_one :through
  • has_and_belongs_to_many

belongs_to and has_one form a one-to-one relationship. has_one :through is a different way to create a one-to-one relationship. has_many and belongs_to form a one-to-many relation. has_and_belongs_to_many or an alternative way has_many :through to create a many-to-many relationship.

belongs_to Association

A belongs_to association sets up a one-to-one connection with another model, such that each instance of the declaring model “belongs to” one instance of the other model. A belongs_to association can be used to setup a one-to-one or one-to-many relationship with other models. For example, consider a cookbook with recipes and categories such that each recipe "belongs to" a particular category.

 class Recipe < ActiveRecord::Base
   belongs_to :category
 end

has_one Association

A has_one association is used to set up a one-to-one connection with another model such that each instance of a model contains one instance of another model. For example if we have two models User and Account, and each User has a single account, then we can use "has_one" to indicate the relationship between the two models.

 class User < ActiveRecord::Base
   has_one: account
 end

has_many Association

A has_many association is used to set up a one-to-may association with other models such that each instance has zero or more instances of another model. In the cookbook example, one category can have many recipes.

 class Category < ActiveRecord::Base
   has_many :recipes
 end

has_many :through Association

A has_many :through model is used to set up a many-to-many association with another model by going through a third model. In this case, the instance of a model can be connected to many instances of another model by proceeding through a third model. For example, consider a medical practice where patients make appointments to see physicians.

 class Physician < ActiveRecord::Base
   has_many :appointments
   has_many :patients, :through => :appointments
 end
 
 class Appointment < ActiveRecord::Base
   belongs_to :physician
   belongs_to :patient
 end

 class Patient < ActiveRecord::Base
   has_many :appointments
   has_many :physicians, :through => :appointments
 end

has_one :through Association

A has_one :through model is used to set up a one-to-one connection with another model by proceeding through a third model. In this case, the instance of a model can be connected to one instance of another model through a third model. For example, each client has one account, and each account has one account history.

 class Client < ActiveRecord::Base
   has_one :account
   has_one :account_history, :through => :account
 end

 class Account < ActiveRecord::Base
   belongs_to :client
   has_one :account_history
 end

 class AccountHistory < ActiveRecord::Base
   belongs_to :account
 end

has_and_belongs_to_many Association

A has_and_belongs_to_many association creates a many-to-many connection with another model without any model in between. In the cookbook example with recipe and category models, if the recipe is allowed to be in more than one category then the has_and_belongs_to_many association can be used.

 class Recipe < ActiveRecord::Base
   has_and_belongs_to_many :category
 end
 class Category < ActiveRecord::Base
   has_and_belongs_to_many  :recipes
 end

Conclusion

Active Record can be used to create the model layer for a web application and can be used to do the following tasks:

  • Create models for applications using the Rails script/generate script.
  • Connect to database and use Rails migrations to create and modify the tables in the database.
  • Search and find records in the database using a number of ActiveRecord methods.
  • Update and delete rows or drop the tables in a database.
  • Model database relations using ActiveRecord methods.
  • Connect various models using associations.

Further Reading

References

  1. Rails Guides
  2. ActiveRecord - The Model
  3. ActiveRecord Pattern - Wikipedia
  4. Kevin Jones - ActiveRecord for Ruby and Rails
  5. Daniel Azuma - Setting the Database with ActiveRecord’s Connection API
  6. Agile Web Development with Rails - Sam Ruby, Dave Thomas, David Hansson, 3rd Edition.
  7. Ruby on Rails Bible - Timothy Fisher