9780596521424
activerecord_id347149.html

Chapter 4. Active Record

Your interfaces to Active Record

The database and its schema

As part of the boot process for your Rails application, Active Record connects to the database for your current Rails environment as specified in config/database.yml. By default, a new Rails application is configured to use SQLite3 databases with zero configuration required on your part.

The way to build your database schema with Active Record is through migrations. These are small classes which incrementally evolve the schema through a convenient interface. Here's one:

class CreatePuppies < ActiveRecord::Migration
  def self.up
    create_table :puppies do |t|
      t.string :name
      t.string :demeanor
      t.date :birthday
      t.integer :owner_id

      t.timestamps
    end
  end

  def self.down
    drop_table :puppies
  end
end

This migration was created by running the Rails model generator from the command line:

$ rails generate model Puppy name:string demeanor:string birthday:date owner_id:integer

We’ll talk more about database migrations and schemas later in this chapter.

Model definitions

Active Record models are defined by subclassing ActiveRecord::Base. Just a bare subclass gives you a lot of functionality for free:

class Puppy < ActiveRecord::Base
end

Without writing anything else, Active Record knows enough to use a table called puppies to store and retrieve data with this model. Each instance of the Puppy class represents a record in the puppies table, and each column in the puppies table is mapped to an attribute on the model. You can add methods, instance variables, or anything else that you can normally add to a Ruby class.

Active Record also provides macros for defining complex but often-needed behaviour on your models quickly and easily, like assocation with other models and validations on particular model attributes. Here are a couple of example macros:

class Puppy < ActiveRecord::Base
  belongs_to :owner
  validates_presence_of :birthday
end

Now, Active Record will look for an integer column called owner_id which refers to the primary key of a record in another table. You can then call an owner method on any puppy to fetch the associated record as an Active Record model object. That second macro is a validation which will prevent the record from being saved if it does not have a value for its birthday attribute.

We’ll discuss model-level associations, validations, and more macros in more detail later in this chapter.

Relations

Beyond having an easy-to-use query interface, Active Record also lets you build relational queries in a modular way:

feisty = Puppy.where(:demeanor => 'feisty')
latest_feisty = feisty.order('created_at DESC').limit(3)

Each chained method call in this expression returns an ActiveRecord::Relation object. These objects represent sets of records in the database. The most common thing to do with a relation is retrieve records from its scope and instantiate them as model objects:

latest_feisty.each {|puppy| puts puppy.name}

For performance reasons, Active Record relations are loaded lazily; until you call some method which requires it, no queries are sent to the database. The above call to each would result in a SELECT statement being executed, but relations can act as scopes for various create/retrieve/update/delete (CRUD) operations:

latest_feisty.create(:name => 'Jack', :birthday => 7.days.ago)
latest_feisty.update_all(:owner_id => sally.id)
latest_feisty.delete_all

If you want to work with the scope of all puppies, you can just call these methods on the model class itself:

Puppy.create(:name => 'Wilbur', :birthday => 10.days.ago, :demeanor => 'smug')

We’ll get into the finer points of performing database queries with Active Record later in this chapter.

Record instances

Once you have an instantiated record object to work with, your interface to the database becomes concerned mainly with managing the attributes of that object and those of its associations. Active Record attributes correspond with the columns found in the model's table, mapped by type. You can access these attributes in typical Ruby fashion:

puppy.name
puppy.name = 'Ben'
puppy.birthday = 1.day.ago
puppy.save

The database isn't updated, however, until the save method is called on the record object. Usually you have to call save explicitly in your code; there are very few situations when Active Record automatically saves an object for you behind the scenes.

When a record does get saved, it runs any validations you've defined for the model to make sure that the values of its attributes don't break any rules you've laid out. If they do, then the save will fail and you can find out why.

Instances of Active Record models with associations set provide methods that make it easy to associate them to other records and let you access those associated records conveniently:

karen = puppy.owner
karen.puppies
karen.puppies << Puppy.last

Associations can be one-to-one, one-to-many, or many-to-many. A plural association lets you access a collection of records and ends up behaving quite a lot like an ActiveRecord::Relation; it is lazily loaded, and you can chain scope and CRUD methods onto it with results that you'd probably expect:

karen.puppies.where(:demeanor => 'whiny').order('name').all
karen.puppies.create(:name => 'Lizzy', :birthday => 1.hour.ago)

Connecting to a Database

Before Active Record can work any of its Object-Relational Mapping (ORM) magic, it needs to know what kind of database it will be working with, and how it's going to connect to that database. In a Rails application, this is defined in a configuration file config/database.yml. Rails loads the options defined in this file, stores them in a hash accessible as ActiveRecord::Base.configurations, and uses them to establish its database connections. By default, a freshly generated app starts off with a simple database setup that lets you get up and running without any manual configuration. Here's what an untouched (and fully functional) database.yml looks like:

# SQLite version 3.x
#   gem install sqlite3
#
#   Ensure the SQLite 3 gem is defined in your Gemfile
#   gem 'sqlite3'
development:
  adapter: sqlite3
  database: db/development.sqlite3
  pool: 5
  timeout: 5000

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  adapter: sqlite3
  database: db/test.sqlite3
  pool: 5
  timeout: 5000

production:
  adapter: sqlite3
  database: db/production.sqlite3
  pool: 5
  timeout: 5000

The adapter properties here are set so that each application environment gets its own SQLite3 database to work with. You will need to have the sqlite3 gem installed before things will work properly. SQLite3 stores databases as single files with locations indicated by the database property of each environment’s configuration, and each database is created automatically if it doesn’t already exist. The pool property determines the maximum number of database connections in Active Record's connection pool. The timeout property is specific to the SQLite3 adapter, setting a timeout for accessing locked tables (in milliseconds).

If you want a new Rails application to be generated with defaults for some other database adapter, you can use the --database option:

$ rails new myapp --database=mysql

This will produce a different database.yml because the MySQL adapter works differently and requires a different set of information to manage its connections.

Unless you are using one of the SQLite adapters, you will probably have to create your application's databases manually. Thankfully, Rails provides a rake task which nicely wraps this chore into a single command:

$ rake db:create

Like many rake tasks in Rails, db:create looks at the RAILS_ENV environment variable to determine which application environment to operate on, and defaults to working with the development environment. This means that the above command would create the development environment's database, with a name (or filename, in the case of SQLite databases) equal to the value of ActiveRecord::Base.configurations['development']['database']. If you want to create databases for all of the environments listed in database.yml in one fell swoop, there's a rake task for that too:

$ rake db:create:all

Tip

Each Rails environment uses a completely separate database configuration, so each could use a different database adapter if that's what you want. The downside to this kind of setup is that differences in the way various database adapters work can remain hidden while you work in development mode, only to produce surprise results in a production environment. As a general rule, it is wise to keep the configurations of your various environments as close to one another as possible.

A Model’s Names

Wouldn’t it be nice if your models were automagically mapped to their corresponding database tables?

Rails does that by way of its convention-over-configuration approach to naming files, classes representing models, and their corresponding database tables. Each model in a Rails application is a subclass of ActiveRecord::Base, and follows the Ruby convention of having capitalized CamelCase class names. The model’s filename should be like the class name, but all in underscored lowercase. That is to say: a School model would be defined in school.rb, and a CourseEnrollment model would be defined in course_enrollment.rb.

The other name associated with a model is its database table name, which by default is the pluralized form of the file’s base name: the School model’s records live in a schools table; CourseEnrollment records live in a course_enrollments table.

Note

Specifically stating which database table a model is to use is easy, but generally not encouraged as it makes understanding the codebase harder. If you’re not having to support legacy systems, you might as well keep things simple.

Developing your Database Schema with Migrations

After your application has a database to work with, you can start populating it with the tables, columns, and indexes that define your schema. The primary means of developing the schema is by creating a number of small Ruby files called migrations. A typical Rails application will end up with a growing number of migrations as development progresses and the schema evolves. Each migration forms a link in a sequential chain of alterations to the database schema. The migration files are stored in db/migrate and each one usually consists of just a single class with two method definitions: up and down. Here's an example of what generating a migration looks like in the terminal:

$ rails generate migration CreateUsersAndClouds
      invoke  active_record
      create    db/migrate/20110918024931_create_users_and_clouds.rb

We gave the generator the name of the migration, and it created a single file for us. The long string of numbers at the beginning of the filename is a timestamp from whenever the migration is generated. This is how Rails maintains a consistent ordering of migrations and keeps track of which migrations have already been run. Here is the migration skeleton which has been generated for us:

class CreateUsersAndClouds < ActiveRecord::Migration
  def up
  end

  def down
  end
end

The file contains a subclass of ActiveRecord::Migration using the name we passed to the generator. The up method is going to contain the changes we want to make to our schema; the down method will define how those changes can be undone. So for example, a create_table call in the up method should be probably be paired with a corresponding drop_table call in the down method.

Note

Instead of writing up and down methods, you can write a single change method and not have to worry about how to undo your changes. The catch is that only certain migration methods (described in greater detail later in this chapter) are supported within a change method:

  • add_column

  • add_index

  • add_timestamps

  • create_table

  • remove_timestamps

  • rename_column

  • rename_index

  • rename_table

So for example, if you use remove_column in a change method and try to roll back the migration, you'll get an ActiveRecord::IrreversibleMigration error which will abort the rollback.

Type Mapping

Different databases implement different sets of types which each have different properties, so Active Record defines a single set of abstract data types which map to both database types and Ruby classes. Your migrations and schema concern themselves with these simplified types, which are listed in the table below along with their corresponding types in SQLite databases and in Ruby. Column types for database like MySQL are similar.

Table 4.1. Data types in Active Record

Abstract TypeSQLite TypeRuby Class
integerintegerFixnum
decimaldecimalBigDecimal
floatfloatFloat
stringvarchar(255)String
texttextString
binaryblobString
datedateDate
datetimedatetimeTime
timestampdatetimeTime
timetimeTime
booleanbooleanTrueClass/FalseClass

Tables

The create_table method takes a name for the new table and yields an object representing the table to a block that you provide. From there, you can define columns for the new table in a few different ways. Here is a very minimal table definition:

create_table :people do |t|
  t.string :name
end

The string method is used to create a string column called "name". Behind the scenes, the table is also automatically given an auto-incrementing integer column called id to use as its primary key.

The create_table method can take an options hash for you to tailor your table to meet specific requirements. If you are creating a join table for a has_and_belongs_to_many relationship, for example, it should not have a primary key column:

create_table :weblogs_users, :id => false do |t|
  t.integer :weblog_id, :user_id
end

Here are the options available on create_table:

:id

Set to false if you don't want this table to get the default primary key id column.

:primary_key

Unless you've set :id to false, this option specifies the name of the automatically added primary key column.

:options

This is a string of table options which gets sent directly to the database. If you wanted to create a MyISAM table in your otherwise-InnoDB MySQL database, for example, you could pass :options => "ENGINE=MyISAM".

:temporary

Set to true if you are creating a temporary table.

:force

Set to true if you want any existing table of the same name to be dropped when this table is created. Defaults to false, which means you'll get an error from the database in that situation.

Shortly after creating a table in your database, you are bound to want to change it. One way is the change_table method. It yields an object representing the table to a block that you provide, just like create_table does. However, the table object yielded by change_table is more flexible, allowing for a wider range of schema changes:

change_table :people do |t|
  t.string :email
  t.index  :email, :unique => true
end

Note

The object yielded by create_table is an instance of ActiveRecord::ConnectionAdapters::TableDefinition, while change_table yields an instance of ActiveRecord::ConnectionAdapters::Table. The former provides access to all the operations available in SQL for defining a table in a single CREATE TABLE statement in SQL; one call to create_table results in just one query sent to the database. A call to change_table, on the other hand, results in one query for each schema modifying method called on the yielded Table object.

To get rid of a table, just use drop_table:

drop_table :people

Columns

Most of your database's columns will probably be created in create_table blocks. You can use any of the abstract data types listed in the section on type mapping (see the section called “Type Mapping”).

create_table :users,  do |t|
  t.string  :name
  t.string  :email
  t.string  :password
  t.date    :birthday
end

If you wanted to be more verbose in creating that users table, you could write its migration using the general-purpose column method instead:

create_table :users do |t|
  t.column :name,        :string
  t.column :email,       :string
  t.column :password,    :string
  t.column :birthday,    :date
end

After a table has been created, you can add and remove columns in a couple different ways. The most succinct approach if you're just making one or two changes is to use the add_column and remove_column methods. The add_column method takes the table name, column name, and column type. The remove_column method only needs the table and column names:

add_column    :users, :active_subscription_id, :integer
remove_column :users, :password

Each method for adding columns takes an options hash as an optional argument. Here are the options available for columns:

:limit

This option specifies the number of bytes used to store values of this column.

:default

The default value of this column for new database records and new Active Record instances.

:null

Set this option to false to disallow NULL values in this column. The default for this option is true, which allows NULL values.

In addition to these general-purpose options, there are two options specific to decimal columns:

:precision

Specifies the number of significant digits in a decimal column. A value of 123.45 has a precision of 5.

:scale

This is the number of digits stored after the decimal point for values in a decimal column. A value of 123.45 has a scale of 2.

You can change any of these options on a column after it has been created, too. One way is the change_column method:

change_column :users, :email, :null => false

If you've got a number of columns to manipulate on the same table, you might prefer the change_table method instead of the one-off versions:

change_table :users do |t|
  t.change  :email, :null => false
  t.boolean :admin
  t.remove  :birthday
end

primary_key

To add a custom primary key to your table, you can use the primary_key method on the object yielded by create_table, passing it the name of the primary key column. You'll need to pass :id => false to create_table so that Active Record doesn't try to create the default primary key column too.

create_table :things, :id => false do |t|
  t.primary_key :number
end

timestamps

The timestamps method is a handy shortcut which creates two datetime colums, created_at and updated_at, which Active Record will update automatically when a record is created or updated.

change_table :users do |t|
  t.timestamps
end

This method is available in both create_table and change_table blocks. Timestamps can be removed in a change_table block with remove_timetamps.

references (belongs_to)

The references method (aliased as belongs_to) adds the necessary foreign key column for the given belongs_to association.

create_table :plants do |t|
  t.belongs_to :species
end

This code would add a species_id column to a new plants table, to be later used in a belongs_to association defined in the Plant model.

You can also add a type column, necessary for polymorphic associations, by passing in the :polymorphic => true option. The following code would add two columns: owner_id and owner_type:

change_table :plants do |t|
  t.belongs_to :owner, :polymorphic => true
end

To remove foreign key columns, use remove_references/remove_belongs_to.

Associations

One of the key differences between relational databases and object-oriented systems is the way that hierarchical data is represented. In an object-oriented program, a Person object might have many Puppy objects associated with it as pets, with the Person keeping track of its puppies by storing references to them in a collection object of some kind.

In a relational database, however, the child entities keep track of the parents: each row in the puppies table would reference an associated row in the people table by storing its owner’s primary key in a special column in the puppies table. As long as an index is maintained for the foreign key column, queries to find either a particular student’s school or a particular person’s puppies can be completed very quickly without the database having to look at each student row one at a time.

Active Record makes all this very easy to deal with, letting you type out things like person.puppies and get back what you expect. Here's how you make it happen:

class Person < ActiveRecord::Base
  has_many :puppies
end

Besides the people table itself, all this requires is a puppies table with a column called person_id. The has_many method is a class method on ActiveRecord::Base which defines a public instance method on Person called puppies. This method returns an array-like object which gives access to all the puppies which have the person's id in their person_id column.

By declaring the has_many macro in the Person class, we are defining an association on that class, and Active Record does a fair bit of work behind the scenes to keep track of the association and provide convenient ways for you to manage a record’s relationships with other records. In order to get the same kind of convenience on the other side of the puppy-owner relationship, we would make a similar declaration in the Puppy class:

class Puppy < ActiveRecord::Base
  belongs_to :person
end

Note

Associations don’t really need to be reciprocated; a has_many declaration does not need a corresponding belongs_to in the associated model in order to function correctly. It does however make reading the codebase easier if you declare both sides of an association.

belongs_to

A belongs_to association is defined on a model with a foreign key. This declaration is used for both one-to-one and one-to-many associations; a model with a belongs_to declaration doesn't care whether or not there are other records in its table with the same foreign key. Continuing the example above, let’s modify the Puppy class to make it clear who’s the pet and who’s the owner:

class Puppy < ActiveRecord::Base
  belongs_to :owner, :class_name => "Person"
end

Instead of person_id, Active Record now looks for an owner_id column on the puppies table. We still want to use our people table (not an owners table), so we pass the :class_name option and then Active Record gets the correct table name from the Person class.

A belongs_to association adds a few instance methods to its model. Here’s what we get on instances of Puppy from the above declaration:

owner

Returns a Person object representing the associated person record in the database; that is, the person with an id equal to the owner_id attribute in the Puppy object. Returns nil if no record is found.

owner=(person)

Assigns the given Person object to the Puppy object’s association and sets the owner_id of the puppy record to the id of the given person.

build_owner(attributes)

Initializes a new Person object with the given attributes and assigns it to the Puppy, but does not save the newly built object. Since the unsaved person doesn't yet have a primary key assigned from the database, the puppy’s owner_id is left untouched until both halves of the association are saved.

create_owner(attributes)

Like build_owner, but attempts to save both objects in the association to the database after instantiating the objects.

has_one

A has_one association declares a one-to-one association on a model that does not hold the foreign key. If we have a MailingAddress model, we might say that a Person has_one MailingAddress:

class Person < ActiveRecord::Base
  has_many   :puppies
  has_one    :mailing_address
end

This would require the mailing_addresses table to have a person_id column. In most other respects, has_one gives the same functionality as belongs_to.

Note

Keeping the two halves of an association straight can get confusing pretty quickly. In this chapter, when we're differentiating between the model with the foreign key and the model without the foreign key, we'll say parent (the has_one or has_many side, without the foreign key) and child (the belongs_to side, with the foreign key). Conversely, when we're trying to express things relative to the association itself, we'll say base model to mean whichever model has defined the association, and associated or target model to mean the other side of the association.

So for example, the Person class is the parent model in the ownership association reciprocated by the Puppy class, which is the child of that association. From the point of view of the has_many association, Person is the base class (where the association is defined) and Puppy is the associated class. If we're talking about the corresponding belongs_to association, though, we'll call Puppy the base class and Person the associated class.

Options

Many options are shared between the various association macros. Here's a listing of them, grouped by which associations they apply to.

has_one/has_many/belongs_to

:autosave

If :autosave is true, two new things will happen when instances of this class are saved. First, any objects in the association will also be saved. Second, associated objects which have had the mark_for_destruction method called on them will be destroyed. Defaults to false.

:class_name

This option specifies the class name of the associated objects (as a String), and is only needed if the class name is not simply the CamelCase version of the name given to the association.

class Student < ActiveRecord::Base
  belongs_to :school
  has_one    :address, :class_name => 'MailingAddress'
end
:conditions

Sets extra conditions required for records to be included in the association. Takes a hash, array, or string, just like in the various finder methods. If a hash is used, the association's create and build methods will automatically set the required attributes for new records as they are defined in the conditions hash. For example:

class Student < ActiveRecord::Base
  has_many :canadian_addresses, :class_name => 'MailingAddress',
                                :conditions => {:country => 'CA'}
end

In this case, student.canadian_addresses would only return the student's addresses in Canada, and student.canadian_addresses.build would initialize a MailingAddress with country already set to "CA".

:dependent

Determines what to do with associated objects when instances of this class are destroyed.

  • The :destroy setting calls the destroy method on each associated object when the parent is destroyed.

  • When set to :delete (for a belongs_to or has_one association) or :delete_all (for has_many associations), the associated records are deleted from the database without using the destroy method. This means that no callbacks are run before deletion (such as from a :dependent option on another association), so this setting should be used with caution. On the other hand, considerable processing time is saved by not having to first load the associated records into memory in order to perform the callbacks.

  • For has_one and has_many associations, :dependent => :nullify simply sets the relevant foreign keys of the associated records to NULL.

By default, the associated objects are left alone.

:foreign_key

Specifies the name of the foreign key used for this association. For has_one and has_many associations, this defaults to the singular underscored name of the current class with "_id" appended. For belongs_to associations, the default is the name of the association with "_id" appended. A contrived example:

class School < ActiveRecord::Base
  has_many :students,
           :foreign_key => 'facility_id'
           # overrides default of 'school_id'
end

class Student < ActiveRecord::Base
  belongs_to :university,
             :class_name => 'School',
             :foreign_key => 'facility_id'
             # overrides default of 'university_id'
end
:include

Specifies second-order associations which are eager-loaded with this association in the same manner as Active Record's finder methods.

:readonly

When :readonly is true, objects loaded through this association cannot be saved.

:select

Takes a string of comma-separated column names to select when loading the associated objects from the database, just as in ActiveRecord's finder methods. The default is to select all columns. When using this option, you must include the primary key in the string if you want to be able to save the associated objects.

:validate

If set to true, the associated objects are validated along with the parent object when it is saved.

Better Names for your Associations

Associations always represent some kind of role being filled for one entity by another. A user might be the author of several articles, and also be the organizer of several events.

We can make most of these roles explicit by using the :foreign_key and :class_name options to give the belongs_to associations clear names:

class User < ActiveRecord::Base
  has_many    :articles,  :foreign_key => :author_id
  has_many    :events,    :foreign_key => :organizer_id
end

class Article < ActiveRecord::Base
  belongs_to  :author,    :class_name => 'User'
end

class Event < ActiveRecord::Base
  belongs_to  :organizer, :class_name => 'User'
end

Now we can use event.organizer or article.author and behind the scenes, Active Record will know to use the users table for both lookups.

Polymorphic associations

Suppose we want our users to be able to post comments on any of the articles or events in our database. The Comment model will need a belongs_to association with whatever is being commented on, and both articles and events should be able to fill that role. Active Record handles this with polymorphic associations, which are declared in a pretty straightforward way:

class Article < ActiveRecord::Base
  has_many    :comments,  :as => :subject
end

class Event < ActiveRecord::Base
  has_many    :comments,  :as => :subject
end

class Comment < ActiveRecord::Base
  belongs_to  :subject,   :polymorphic => true
end

In order for Active Record to know which table to look in to find a comment’s subject, we need to make sure the comments table includes a subject_type column (of type string) in addition to its subject_id. The subject_type gets filled with the class name of the associated object, so that Active Record knows to look in the right table.

Attributes

A database row's column values are mapped to attributes on an Active Record object. Active Record uses the schema of the model’s table to figure out what attributes a record will have.

The most common way to access attributes is by using the standard getters and setters defined dynamically by Active Record:

puppy.name     # => "frank"
puppy.demeanor # => "grizzled"
puppy.demeanor = "repentant"

The attribute values returned from these accesssors have already run through Active Record’s type conversion process (see the section called “Type Mapping” for a list of which ruby types get returned from which columns).

Tracking Changes

Sometimes you want to know what kind of changes have been made to a model object since it was instantiated. When a model object’s attributes have been altered, we say that it's a dirty object. If a dirty object is saved to the database, then it becomes clean again. You can use the changed? method to find out if an object is dirty.

student.changed?                 # => false
student.middle_name = 'Ambrose'
student.changed?                 # => true
student.save
student.changed?                 # => false

You can also get more specific and ask about particular attributes and what their values were the last time the object was saved (or instantiated, if it hasn’t been saved yet).

student.gpa = 3.22
student.gpa_changed?             # => true
student.gpa_was                  # => 3.14
student.gpa_change               # => [3.22, 3.14]
                

If you want to get information on all of an object’s changes, you can use the changed and changes methods:

student.middle_name = 'David'
student.changed
  # => ["gpa", "middle_name"]
student.changes
  # => {"gpa" => [3.22, 3.14], "middle_name" => ["Ambrose", "David"]}

If you modify an attribute without using assignment, Active Record will not notice the changes automatically. If you need the changes to be picked up, you can be explicit about it:

student.save
student.middle_name_will_change!
student.middle_name_changed?     # => false
student.middle_name << 'son'
student.middle_name_change       # => ["David", "Davidson"]

But really, mutating your attributes in-place is best avoided altogether.

Validations

Active Record uses Active Model’s validation system to check a record’s attributes before it is saved to the database.

Validations are run automatically before an Active Record object is saved to the database, and if any errors are found then the save will fail. You can also check the validity of an object whenever you want by calling valid? on that object. Whenever an object’s validations are run, any resulting errors are accessible through its errors accessor.

Built-in validations

Active Model and Active Record provide a bunch of commonly used validations for you to use in your model classes. You can declare them either with an individually-named class method, or by passing an option to the validates method. Here are two equivalent ways of coding the same model:

class User < ActiveRecord::Base
  validates :email, :presence => true, :format => {:with => /@/}
end

class User < ActiveRecord::Base
  validates_presence_of :email
  validates_format_of :email, :with => /@/
end

You can pass multiple attribute names at once to define the same validations for all of them:

class User < ActiveRecord::Base
  validates :home_email, :work_email, :presence => true
  validates_format_of :home_email, :work_email, :with => /@/
end

As you can see, using a single validates method can be a more concise approach when there are multiple validations on a single attribute. On the other hand, using the class methods can sometimes make things more readable. In the examples below, we'll show both forms.

Note

The attributes being validated here don't have to be backed by database columns or anything else in particular. They could be associated models, in-memory attributes, or really any method on the model which takes no arguments and returns some value.

acceptance

This validation solves the problem of making sure that a user has accepted some agreement or terms of service. The typical way of confirming an agreement like this is with a check box in a form. When a controller passes the parameters from such a form to update the attributes of a model, the value of the checkbox is passed in as well. The value of the checkbox doesn't need to be saved to the database, though, so this validation is not performed on a column attribute but rather on an in-memory attribute which is automatically added to the model.

class User < ActiveRecord::Base
  validates :acceptance => :terms_of_service
  # or:
  # validates_acceptance_of :terms_of_service
end

For this example, the check box could be constructed in its form like this:

<% form_for @user do |user| %>
  ...
  <%= user.check_box :terms_of_service %>
<% end %>

This validation takes all the standard options for validations like :message and :on, plus one other:

:accept

Specifies the positive value expected for the agreement attribute. By default, it is "1", which is the default value submitted by a checked check box in Rails.

associated

ActiveRecord only

This validates the specified associations as part of the current model's validation. If the associated records don't pass their own validations, then this validation adds an error specifying that.

class User < ActiveRecord::Base
  has_one :mailing_address

  validates_associated :mailing_address
  # validates :mailing_address, :associated => true
end

Warning

If you get a SystemStackError from validating one of your models, it might be because you've put a validates_associated on both ends of an association, each referencing the other. It causes an infinite loop back and forth, so don't do that.

confirmation

This validation helps with the UI pattern of asking a user to input a piece of data twice to be extra sure they typed it correctly. This is very similar to validates_acceptance_of, in that it validates the value of an automatically generated attribute.

class User < ActiveRecord::Base
  validates_confirmation_of :password
  # validates :password, :confirmation => true
end

In this example, the generated attribute would be called password_confirmation. In the view, you'd render a password field and a password_confirmation field, and the validation would add an error if the two aren't equal.

exclusion

Validates that the values of the given attributes are not found in some set of banned values. The blacklist can be represented by any Enumerable object, passed to the macro with the :in option:

class User < ActiveRecord::Base
  validates_exclusion_of :name,
    :in => ['Kirk', 'Spock', 'Bones']
  # validates :name, :exclusion => {:in => ['Kirk', 'Spock', 'Bones']}
end

format

Checks the given attributes against a regular expression, adding an error if the value does not match.

class User < ActiveRecord::Base
  validates_format_of :email, :with => /@/
  # validates :email, :format => {:with => /@/}
end

inclusion

Inverse of validates_exclusion_of, ensuring that the values of the given attributes are included in a set of allowed values.

class User < ActiveRecord::Base
  validates_inclusion_of :date_of_birth,
    :in => (130.years.ago.to_date .. Date.today),
    :message => "must be at least plausibly human"
  # validates :date_of_birth, :inclusion => {
  #   :in => (130.years.ago.to_date .. Date.today),
  #   :message => "must be at least plausibly human"
  # }
end

length/size

Validates the length of the given attributes' values. This calls the length method on the value of the attribute, so it will mean different things for strings as opposed to arrays or other enumerables. If the value doesn't respond to length, then the validator calls to_s on the object and then gets the length of the resulting string. You can specify a Range of allowed values with the :in option, as in validates_inclusion_of, or you can just specify a :minimum or :maximum value.

class User < ActiveRecord::Base
  validates_length_of :password, :in => 6..255
  # validates :password, :length => {:in => 6..255}
end

In addition to a general :message option, you can specify :too_short and :too_long messages which are used in each case.

Warning

This validation is not a good way of limiting the number of elements in an Active Record association, even if the child model has a validates_associated pointing at the parent. This is because of the order in which validations are run, associations are reloaded, and models are saved. If you need to limit the size of an association, a custom validation could go on the child model which checks the count of existing sibling records in the parent's association.

numericality

Validates that the values of the given attributes are numbers of some kind, or strings which represent numbers. This macro also lets you ensure that the value follows other constraints like being greater than or less than particular limits.

class Product < ActiveRecord::Base
  validates_numericality_of :price,
    :greater_than_or_equal_to => 0.00,
    :message => "must be a non-negative number"
  # validates :price, :numericality => {
  #   :greater_than_or_equal_to => 0.00,
  #   :message => "must be a non-negative number"
  # }
end

ere are some special options for this validation:

:only_integer

allows only integer values.

:greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to

constrains the value to the specified limits.

:odd, :even

ensures that the value is either odd or even.

presence

Validates that the given attributes are not blank, by calling blank? on their values. This validation is not implicitly included by any other validations, so if you need a particular attribute to be present, you should use this validation in addition to any others you have for that attribute.

class User < ActiveRecord::Base
  validates_presence_of :name, :email, :password
  # validates :name, :email, :password, :presence => true
end

uniqueness

ActiveRecord only

Validates that the values of the specified attributes are not duplicated by other records, either across the whole table or within the scope of particular columns.

class User < ActiveRecord::Base
  validates_uniqueness_of :email
  # validates :email, :uniqueness => true
end

class Log < ActiveRecord::Base
  validates_uniqueness_of :name, :scope => :user_id
  # validates :name, :uniqueness => {:scope => :user_id}
end

Here, a Log record will fail validation if it has the same name as another log with the same user_id, and a User record will fail validation if any other user in the database is already using the same email address.

Warning

Application-level validations can only go so far when attempting to constrain values based on the state of other records in the database. When multiple processes are accessing the database at the same time, race conditions can easily break a uniqueness validation. A robust solution would be to maintain a unique index on the appropriate columns in the database, in addition to a uniqueness validation as described here. If and when a race condition occurs which lets a uniqueness validation pass inappropriately, a unique index means that the database rejects the offending UPDATE or INSERT statement with a duplicate key error. If you are using one of the built-in MySQL, SQLite, SQLite3, or PostgreSQL adapters, then that gets thrown in Rubyland as a ActiveRecord::RecordNotUnique exception. If you aren't using one of those adapters, you will end up with an ActiveRecord::StatementInvalid exception being thrown. In order to handle that kind of exception accurately, you need to parse it to find out that it refers to a duplicate unique key instead of some other database error.

Common options

Custom validations

each

Validates each named attribute with a given block of code. The block is called with three arguments: the model instance, the symbol of the attribute being validated, and the value of that attribute. The block is responsible for adding errors to the model instance as appropriate.

class User < ActiveRecord::Base
  validates_each :first_name, :last_name do |record, attribute, value|
    if ['Kirk', 'Spock', 'Bones'].include?(value)
      record.errors.add(attribute, "must not have served on the Enterprise")
    end
  end
end

When Validations Don’t Happen

There are some cases when a model object is saved to the database and yet validations don’t get run. The simplest way to bypass validations is call save and pass it the :validate => false option. Note that you can't do this with save!, since the whole point of that bang-suffixed version of the method is to raise exceptions when validations fail.

student.gpa = 9000
student.save(:validate => false)

Here are the other instance methods which update records but don't run validations:

  • decrement!

  • increment!

  • toggle!

  • touch

  • update_attribute

  • update_column

Note that all of these methods are concerned with updating a single attribute at a time; this is a good way to remember when validations are being skipped on instance updates. Contrast with update_attributes and update_attributes!, which both take a hash full of attribute values and do indeed run validations before trying to save anything to the database.

There are also a few class methods which update records without validating first:

  • decrement_counter

  • increment_counter

  • update_all

  • update_counters

Of these, update_all is probably the most commonly used in application code. This class method is intended to be used as a fast way of updating many records at once, so the whole thing is accomplished with a single query and the tradeoff is that validations and callbacks cannot be run.

Callbacks

Callbacks are hooks which let you run some code when particular events like creation and destruction happen to a model object. You can define callbacks on a model in two different ways. One way is to define an instance method on the model class, naming the method after the callback in question:

class Student < ActiveRecord::Base
  def before_destroy
    AlumniMailer.deliver_request_for_donations(self)   
  end
end

Another way is to use the callback macros, which are Active Record class methods named after the various callbacks. Callback macros take a few different kinds of arguments: symbols, Procs, and callback objects. You can pass as many of these arguments as you want in any combination. The most common type of argument is a symbol:

class Student < ActiveRecord::Base
  before_create :generate_card_number

  protected
  def generate_card_number
    self.card_number = "1%02d%06d" % [rand(100), self.id]
  end
end

The symbol must refer to an instance method on the model object, which is run when the callback is triggered. Similarly, Proc objects which have been passed to the callback macro will be executed directly.

There are seven different events for which ActiveRecord has built-in callbacks:

  • save occurs when a record is saved to the database.

  • create occurs when a new record is saved to the database for the first time.

  • update occurs when an existing record is saved.

  • validation occurs whenever a record is validated. This happens as part of any save operation, but it also happens in other circumstances such as when you explicitly call valid? on an object.

  • destroy occurs when any record is destroyed.

Single Table Inheritance

Rails projects tend to have rather flat class hierarchies. Most of the time, Ruby makes it easier to implement and maintain shared behaviour by using mixin modules instead of a rigid inheritance tree. If you have two Active Record models which both have many of the same attributes, you can define a common interface for them in a module that you include in both classes.

Sometimes, however, you have multiple entities in your data model which are just so similar that it seems silly to keep them apart. Active Record lets you do this through its implementation of the single table inheritance pattern (STI): if you give one of your tables a type column, Active Record will use it to store the class name of the model associated with that record. Any time that record is retrieved from the database, Active Record will instantiate it as the appropriate class. You can then develop a little family of model classes and they'll be managed in a pretty intuitive way.

Here’s an example that could be part of a very minimal microblogging app:

class Log < ActiveRecord::Base
  has_many :entries, :order => 'created_at DESC'
  has_many :idea_entries
  has_many :image_entries
  has_many :video_entries
end
class Entry < ActiveRecord::Base
  belongs_to :log
  attr_accessible :title
end
class IdeaEntry < Entry
  attr_accessible :content, :subject_url
  validates_presence_of :content
end
class ImageEntry < Entry
  attr_accessible :subject_url
  validates_presence_of :subject_url
end
class VideoEntry < Entry
  attr_accessible :subject_url
  validates_presence_of :subject_url
end

Now we can call Log.entries and get all of the associated IdeaPost, ImagePost, and VideoPost records together, sorted in reverse chronological order, instantiated as the correct classes, all with only one database query. If we'd kept the different types of posts in separate tables, we would have to use three separate associations with three database queries, and then combine them and sort them ourselves.

Note

When should you use STI, and when should you just share behaviour with modules? Many situations could be handled by both approaches, but here's a good rule of thumb: if records of different types are frequently retrieved and operated on together, consider using STI.

Aggregation

ActiveRecord lets you go beyond its default type mapping if you need to give your models rich attributes with custom behaviour of their own. These are called aggregations, and you define them with the macro composed_of. Here's what it looks like:

class Person < ActiveRecord::Base
  composed_of :hair_color, :class_name => 'Color', :mapping => [:hair_color, :to_s]
end

Similar to the association macros, the ultimate result of composed_of is the definition of instance accessor methods for the new aggregated attribute. The method’s first argument is the name you choose to give the aggregated attribute you are defining. The second argument is an options hash, which almost always contains a :mapping option designating the pairs of corresponding attributes between the Active Record model and the aggregated object. It also needs a :class_name option if the name of the aggregated attribute's class is not simply the CamelCase of the first argument.

The :mapping option in the above code would rely on the Person model already having a hair_color attribute. Calling person.hair_color would try to instantiate a new instance of the class Color, which you would have to define elsewhere. The constructor method would be called with the value of the hair_color attribute on that particular instance of Person.

person.hair_color
# roughly equivalent to:
# Color.new(person.read_attribute(:hair_color))

person.hair_color = Color.new("red")
# roughly equivalent to:
# person.write_attribute(:hair_color, Color.new("red").to_s)

Creating

You can create an Active Record object the same way you would with most other Ruby objects: by calling the new method on the appropriate class:

cloud = Cloud.new
cloud.inspect
# => "#<Cloud id: nil, contributor_id: nil, url: nil, looks_like: nil, created_at: nil, updated_at: nil>"

Nothing is saved to the database yet; all we've done is create an instance of Cloud in memory. Because we didn’t specify any of the values for its attributes, the new object is initialized with nil for each of them. Even the primary key id attribute is blank at this point, since it is determined only when the object is saved to the database as a record. You can also pass a hash of attribute-value pairs to Cloud’s initializer if you want to start the object off with some novel data. Either way, once the object is created you can access its attributes via accessor methods generated automatically by Active Record:

cloud = Cloud.new(:filepath => 'neato_dumbo_cloud.jpg')
cloud.looks_like = 'elephant'

Once the cloud’s attributes are how we want them, we can save the cloud to the database:

cloud.save
# => true

Active Record’s save method tries to save the model as a record in the database. If it is saved successfully, it returns true. If it fails for any reason, it returns false. The most common way for a save to fail is because the model's validations have not been satisfied correctly. See the section called “Validations” for details on how to define validations for your models. There's also a whiny version of the method which will raise an exception if the save encounters any errors:

cloud.save!
# => true

Finding the records you want

There are a number of ways available to look up existing database records and generate Active Record objects from them. There is one class method available on each of your models which serves as the primary interface for looking up records. This method is called find, and operates in one of a few different modes depending on the first argument passed to it.

If the first argument is an integer, you will get new Active Record instances corresponding to the record with the given id column value(s):

Cloud.find(1)
# => #<Cloud id: 1, filepath: 'neato_dumbo_cloud.jpg', 
  looks_like: "elephant", created_at: "2009-02-16 22:23:10", 
  updated_at: "2009-02-16 22:23:10">

Cloud.find(1,2)
# => [#<Cloud id: 1, filepath: 'neato_dumbo_cloud.jpg', 
  looks_like: "elephant", created_at: "2009-02-16 22:23:10", 
  updated_at: "2009-02-16 22:23:10">, 
  #<Cloud id: 2, filepath: 'weird_cloud.jpg', 
  looks_like: "heart", created_at: "2009-02-16 22:23:10", 
  updated_at: "2009-02-16 22:33:06">]

If any of the given id values do not correspond to those of existing records in the clouds table, then the find method will raise an ActiveRecord::RecordNotFound error. If you don't know or don’t care what a cloud’s id is, you can get the first or last cloud in the database with first and last:

Cloud.first
# => #<Cloud id: 1, filepath: 'neato_dumbo_cloud.jpg', looks_like: "elephant", 
  created_at: "2009-02-16 22:23:10", updated_at: "2009-02-16 22:23:10">

Cloud.last
# => #<Cloud id: 2, filepath: 'weird_cloud.jpg', looks_like: "heart", 
  created_at: "2009-02-16 22:23:10", updated_at: "2009-02-16 22:33:06">

These method calls would each return nil if the clouds table were empty; they won’t raise an exception like they would if they were given specific ids. You can get an array of all clouds in the database with all:

Cloud.all
# => [#<Cloud id: 1, filepath: 'neato_dumbo_cloud.jpg', looks_like: "elephant", 
  created_at: "2009-02-16 22:23:10", updated_at: "2009-02-16 22:23:10">, 
  #<Cloud id: 2, filepath: 'weird_cloud.jpg', looks_like: "heart", 
  created_at: "2009-02-16 22:23:10", updated_at: "2009-02-16 22:33:06">]

If the clouds table were empty, this method call would just return an empty array.

No matter which mode you are using with find, you can alter the way records are retrieved by providing a hash of options as the last argument. Almost all of these options directly correspond with fragments of SQL which are incorporated into the the SELECT query generated by ActiveRecord.

Constructing Relations

Active Record relations let you construct SQL queries in a flexible way. The simplest relation of a model represents all of its table’s rows and columns. You can access this relation with the model’s class method scoped:

Puppy.scoped
# => <#ActiveRecord::Relation ... >

A relation’s inspect method ends up instantiating a collection of Active Record objects based on the results of its query. If you want to verify the logic of a relation instead of fetching its contents, you can easily look at the SQL that is being constructed:

Puppy.scoped.to_sql
# => "SELECT ..."

The SQL returned here is a SELECT statement, but relations may also be used to execute creations, updates, and deletions. Relations are constructed in a modular fashion by chaining together query-builder methods. Each method returns a new relation object based on the one returned by the previous method. The order of the methods does not matter: you could put a select after a group, even though

Most of the query builder methods directly correspond to clauses available in an SQL SELECT statement, like WHERE and GROUP BY. You don't need to call scoped first to get the base relation, you can just call the query builder methods on the ActiveRecord::Base object itself.

Puppy.where(:demeanor => "dismissive").group(:name).order("count(*) DESC").to_sql

You can also build relations off of has_many associations:

kathy.puppies.where(:demeanor => "vigilant").to_sql
# => "SELECT ..."

If you've

Figure 4.1. A Relation and its SQL

A Relation and its SQL

where

The where method allows you to place limits on which records are retrieved from the database. You can supply these conditions as a string, an array, or a hash, but in the end it will always end up as a WHERE clause in the SELECT query which gets sent to the database. Passing in a hash is arguably the most Ruby-like way to handle conditioned queries, so it shouldn't be surprising that it looks very little like the SQL that it eventually gets translated into:[1]

Cloud.where(:looks_like => 'elephant').to_sql
# => SELECT * FROM "clouds" WHERE ("clouds"."looks_like" = 'elephant')

As you can see, the keys of the conditions hash are interpreted as column names in the model’s table. The associated values can take a few different forms. Most of the time, the supplied object is mapped from its Ruby type to its database equivalent and evaluated for equality with each record’s column value. So in the first example above, Active Record would return all clouds which have a looks_like column value equal to the string "elephant".

If an array is provided, then it is interpreted as a list of possible values:

Cloud.where(:looks_like => ['elephant', 'mouse']).all
# => SELECT * FROM "clouds" WHERE ("clouds"."looks_like" IN ('elephant','mouse'))

In this example, the SQL IN function is used by Active Record to return all clouds with looks_like equal to either "elephant" or "mouse". Similarly, if a range object is provided then the BETWEEN function is used to return only records with column values in the specified range:

Cloud.where(:id => 1..20).all
# => SELECT * FROM "clouds" WHERE ("clouds"."id" BETWEEN 1 AND 20)

Cloud.where(:created_at => (Time.now - 2.weeks)..(Time.now)).all
# => SELECT * FROM "clouds"
#    WHERE ("clouds"."created_at" BETWEEN '2009-02-07 16:30:35' AND '2009-02-21 16:30:35')

It’s nice to be able to use pure Ruby in this way to customize your database queries, but this approach has big limitations which prevent it from being used all the time. For example, there is no way of using the hash syntax to specify “greater-than” conditions, or to match strings with the LIKE function, or to use any of the other functions or operators available in SQL besides IN, BETWEEN, and the equality operator. When more flexibility is required, we can use the array syntax:

Cloud.where(["looks_like LIKE ? AND updated_at > ?", "%#{params[:q]}%", 2.weeks.ago]).all
# => SELECT * FROM "clouds" WHERE (looks_like LIKE '%squirrel%' AND 
  updated_at > '2009-03-01 20:26:16') 

This code would live in a controller action, where we would have access to a params hash of client-supplied parameters . Here we are taking in a query parameter and using it to look up all the clouds updated in the last two weeks that contain the search string in their looks_like column value. The array begins with a fragment of SQL that will form the basis of the conditions. Each question mark in that first string is replaced with the next remaining value in the array, in sequence.

So why don’t we just construct the conditions string ourselves instead of using this odd syntax? The reason is that the array syntax gives Active Record a chance to convert and quote values before they are interpolated into the SQL. In the above example, the Ruby Time object constructed by the 2.weeks.ago expression is converted to an appropriate format and the query parameter is wrapped in single quotes as required by the database. This is obviously desirable for the sake of convenience, but it also has a crucial role to play in the security of your application: unless user-supplied values are properly quoted before being sent to the database, your application is left vulnerable to SQL injection attacks.

If you have a lot of conditions to deal with, it can be hard to keep track of which question mark represents which value. In cases like that, it can be easier to use named placeholders instead of question marks. Then you just need to put the values in a hash keyed with those placeholders:

Cloud.where(["looks_like LIKE :query AND
              updated_at > :updated_since AND
              created_at < :created_until",
              { :query => "%#{params[:q]}%",
                :updated_since => 2.weeks.ago,
                :created_until => 3.hours.ago }
             ]).all
# => SELECT * FROM "clouds" WHERE (looks_like LIKE '%squirrel%' AND
#     updated_at > '2009-03-01 21:40:47' AND
#     created_at < '2009-03-15 18:40:47')

When your query doesn't need any values to be converted or sanitized, you don't need to use an array; you can just provide the WHERE clause of the query as-is:

Cloud.where('updated_at > "2008"').all
# => SELECT * FROM "clouds" WHERE (updated_at > "2008")

select

relation.select(SQL)

The select method determines the attributes available on fetched records. It takes one SQL fragment as its argument, which is passed to the generated SELECT clause. The default is to select all columns from the model's table.

Puppy.scoped.to_sql
# =>
Puppy.select("id, name").to_sql
# =>

Note that your choice of attributes here can have rather sweeping effects on the behaviour of the objects you get back. If you leave out a record's primary key, for example, Active Record will not be able to save or reload it.

When model objects are instantiated from the rows that the database returns, Active Record constructs each object’s attributes from whichever columns are present. If you select based on exotic SQL expressions or columns from other tables, those values are present in the objects' attribute hashes, keyed to their column names:

puppies = Puppy.select("id, name, people.name as owner_name").joins(:owner)
puppies.to_sql
# =>
puppies.first["owner_name"]
# =>

group & having

relation.group(*columns).having(*conditions)

Constructs a GROUP BY clause for the relation, collapsing multiple rows which have the same values for the specified columns and letting you fetch aggregate calculations on the grouped rows. If you are using this relation to instantiate Active Record objects, then you'll just get one record back from each group (whichever one your database decides to give you).

If you end up performing an aggregate calculation such as a count or sum on this relation, you'll get an OrderedHash associating grouped column values with the results of the calculation for that group.

When using a group in your relation, a call to having allows you to constrain the result set based on aggregate calculations on the grouped rows.

Puppy.group(:name).having("count(name) > 2").to_sql
# => "SELECT \"puppies\".* FROM \"puppies\"  GROUP BY name HAVING count(name) > 2"

order

relation.order(*orderings)

Defines how records fetched by this relation are ordered by the database. Each argument is an SQL fragment which is used in a generated ORDER BY clause.

limit & offset

relation.limit(limit).offset(offset)

These methods define a particular “page” of records to be fetched by this relation. The limit is the number of records in the page, and the offset is the number of records skipped. You can use a limit without an offset, but an offset on its own will result in invalid SQL.

includes

relation.includes(*associations)

Use the includes method to load a model’s associations. Calling to_sql on a relation with includes values will not return a different SQL query. That's because instead of using a JOIN clause to fetch the associated records, Active Record uses a separate query for each table.

If you want to load associations-of-associations, you can use a hash:

Tumblelog.includes(:posts => [:author, {:comments => :author}])

This ends up performing three extra queries to look up the authors and comments of each post, plus the authors of each comment.

joins

relation.joins(*associations)
relation.joins(SQL)

Takes either one or more association names, or an SQL fragment with the full JOIN clause. If association names are provided, then an INNER JOIN is used. If you want to pre-load associated objects, you should use the includes method; joins will not load any associations for you.

lock

relation.lock
relation.lock(locking_clause)

Adds a pessimistic locking clause to the relation’s query. This is only available for databases which support row-level locking. For MySQL, the default behaviour is to append FOR UPDATE to the SELECT statement.

Note

You can also lock individual rows by calling the lock! method on a record object. This method takes an optional custom locking clause just like its relation-builder equivalent.

readonly

relation.readonly

Active Record objects instantiated through this relation are marked as readonly, and will raise an ActiveRecord::ReadOnlyRecord exception if save is called on them.

from

relation.from(table)

The table to select from. Defaults to the model’s table.

only & except

relation.only(*methods)
relation.except(*methods)

These methods return new relations with some of the clauses of the original removed. For example:

rel = Puppy.where(:name => "Frank").order(:created_at).limit(3)
rel.to_sql
# =>
rel.only(:where).to_sql
# =>
rel.except(:where, :order).to_sql
# =>

Calculations

If you just want to get the results of some aggregate calculations on the records in a relation, it’s going to be a lot faster to let the database do it instead of instantiating a bunch of Active Record objects and iterating over them in Ruby.

relation.calculate(function, column)

The function is the name of an aggregate function in SQL. The most common calculations have their own convenience methods:

relation.average(column)
relation.count(column)
relation.maximum(column)
relation.minimum(column)
relation.sum(column)

If relation was built with a call to group at any point in its construction, then calculate will return an ordered hash which maps the values of the grouped attributes to the results of the calculations. If no group was used, it returns a single value.

Puppy.average(:weight_in_kg)
# => #<BigDecimal ...>
Puppy.group(:name).count
# => #<OrderedHash ...>

Updating

The most common way of updating existing records is to work with individual Active Record objects which have been instantiated from some finder method or from another model object's association. Once you’ve retrieved the records that you’re looking for, you can update their values using the accessor methods that Active Record generates automatically based on your database schema.

column accessor example, ending with a #save

There are also a few type-specific writer methods for integer and boolean attributes:

example: decrement, increment, toggle, ending with #save

Note that no changes are sent to the database unless the save method or one of its variants is called on the object. The most common way of saving record objects is with save or save!, but there are a number of other instance methods which save the record as well:

  • decrement!

  • increment!

  • toggle!

  • update_attribute

  • update_attributes

  • update_attributes!

As you can probably tell from the names of these methods, they all involve updating one or more attributes of the record and then saving the record to the database. For decrement!, increment!, and toggle!, the bang (!) suffix indicates that a save is taking place (no save is made with the bangless versions of those methods). For update_attributes!, however, the bang indicates that the whiny save! is used.

Deleting

The standard way to delete a record is with the destroy method:

student.destroy

In addition to deleting the object’s record in the database, the object's attributes are also frozen to indicate that changes can no longer be propagated to the database with a call to the save method. The destroy method results in the before_destroy and after_destroy callbacks being run. If you just want to delete the record without running any callbacks, you can instead use the delete method:

student.delete

The same DELETE statement is sent to the database, but no callbacks are run, and none of an object's associations are dealt with in any way. This means that if your student objects each have an enrollments association which has been established with the :dependent => :destroy option, nothing will happen to those associated enrollments when delete is called on a student.

Warning

Be very careful whenever you think of skipping callbacks, such as with a call to the delete method. It can be easy to forget about a particular callback which should really be run or association which should really be cleaned up.

Transactions

Some databases support the ability to run multiple updates to the database in a single atomic transaction—a block of code in which statements are guaranteed to either all succeed or all fail. In SQL, a transaction is opened with a BEGIN statement, and either finalized with COMMIT or explicitly aborted with ROLLBACK. In Active Record, this pattern is abstracted into the familiar ruby concepts of blocks and exceptions.

ActiveRecord::Base.transaction do
  david.withdrawal(100)
  mary.deposit(100)
end

To roll back the transaction explicitly, you can raise an ActiveRecord::Rollback error somewhere in the transaction block. The transaction block will rescue the error and send a ROLLBACK to the database. Actually, any error raised within a transaction block will trigger a rollback, but only ActiveRecord::Rollback will be rescued by Active Record. Other errors will continue to propagate up the call chain after the transaction has been rolled back, so you'll still have to handle them somewhere in your application.

If one transaction is nested inside another, the inner transaction block will be ignored by default. This means you should keep a few things in mind when writing transactions that may end up being run within other transactions:

  • Any errors left unhandled from the inner transaction will end up rolling back the outer transaction.

  • If the code surrounding the inner transaction rescues errors from within it, then any queries sent to the database before the error was thrown will not be rolled back.

If you want your inner transaction to handle rollbacks independently of the outer transaction, you can pass the :requires_new option to the inner transaction's method call.

ActiveRecord::Base.transaction(:requires_new => true) do
  david.withdrawal(100)
  mary.deposit(100)
end

Most databases don't support true nested transactions, but Active Record can emulate them by using database savepoints.

Observers

Sometimes the callbacks on your model end up having little to do with the model itself. Consider a few callbacks which create an Event record every time a Puppy record gets created, updated, or deleted.

class Puppy < ActiveRecord::Base
  has_many :events, :as => :subject
  
  def after_create
    events.create(:verb => 'create')
  end
  def after_update
    events.create(:verb => 'update')
  end
  def after_destroy
    events.create(:verb => 'destroy')
  end
end

Every time you open up puppy.rb, you're looking at a bunch of code which has more to do with events than it does with puppies. If you only ever access events through the has_many associations of puppies, then you might just consider Event to be a child model and keep the code where it is. But it would be nice to look at events on their own, as an activity log for the whole app. When there's a logical split like this between the concerns of your callback methods and the core concerns of the model, you should try observers. Here's how you might write an observer for these events, saved as app/models/event_creation_observer.rb:

class EventCreationObserver < ActiveRecord::Observer
  observe Puppy, Person
  
  def after_create(subject)
    Event.create(:subject => subject, :verb => 'create')
  end
  def after_update(subject)
    Event.create(:subject => subject, :verb => 'update')
  end
  def after_destroy(subject)
    Event.create(:subject => subject, :verb => 'destroy')
  end
end

Notice that we are now observing the Person class as well, by specifying it in the call to observe. By default, an observer will look at its own class name to find which model it should register itself with; if we had omitted the observe line, then this observer would have looked for an EventCreation model. But our Event model has a polymorphic belongs_to with both puppies and people, so we can easily use the same code to create events from both.



[1] The SQL shown here was generated by the SQLite3 database adapter. Different adapters necessarily produce slightly different forms of SQL, so a Rails application using another database such as MySQL will show some minor variation which is apparent in the application's logfiles.

Site last updated on: December 11, 2012 at 10:21:28 PM PST
Cover for Rails 3 in a Nutshell

View 2 comments

  1. winterheat – Posted May 17, 2012

    can the Model be the app's data instead of data from the DB?

  2. Edward edward.og – Posted May 21, 2012

    @winterheat this part of Rails is actually based on Martin Fowler’s ActiveRecord pattern written up in Patterns of Enterprise Architecture. I’ll let him explain:

    “An object carries both data and behavior. Much of this data is persistent and needs to be stored in a database. Active Record uses the most obvious approach, putting data access logic in the domain object. This way all people know how to read and write their data to and from the database.”

    In a nutshell, the app’s data is the data in the DB.

Add a comment

View 1 comment

  1. pran – Posted July 26, 2010

    Migrations is an important aspect of active record, and thus, a it's own subsection. Adding a title before this paragraph would suffice.

Add a comment

View 1 comment

  1. pran – Posted July 26, 2010

    I sort of understand what was meant by this definition, but it can be improved.

Add a comment

View 1 comment

  1. cnk – Posted April 9, 2011

    One of those "automatic save" situations is when you save a parent association - but you can't easily say so until you have discussed associations. I might take the "owner" stuff out of the paragraphs about and put that all in a more explicit associations section below - and then mention the cascading save for an association.

Add a comment

View 1 comment

  1. cnk – Posted April 9, 2011

    Why don't you lead with this section? You mention database.yml at the top of the chapter, then go to basic AR use, and then come back to it. Either put configuration before use, or make the database configuration a section at the end of the chapter, kind of like a mini-appendix.

Add a comment

View 1 comment

  1. adacosta – Posted Oct. 20, 2009
    • "to operate on" => "to operate in"
    • sp "withe" => "with the" ; maybe consider replacing with, "in the"

Add a comment

View 2 comments

  1. juwiley – Posted May 23, 2010

    Might be nice to touch on the other rails db: methods, and/or discuss how to list them.

  2. cnk – Posted April 9, 2011

    Or, use this as an opportunity to show folks how to use 'rake -T db' to get a list of all db related methods.

Add a comment

View 1 comment

  1. bitzesty – Posted Oct. 26, 2009

    might be nice to mention names you should avoid, like Task (conflict with rake) or class names which clash with Ruby (Thread) and that singular resource controller still has to be plural

Add a comment

View 1 comment

  1. davesailer – Posted Oct. 22, 2009

    "Each migrations forms a link" should be "Each migration forms a link"

Add a comment

View 2 comments

  1. adacosta – Posted Oct. 20, 2009

    broken paragraph

  2. danrussia – Posted Nov. 25, 2009

    This is a book in progress... they are looking for comments concerning what they have already written. The 'broken paragraph' means they haven't written that section yet. ;)

Add a comment

View 2 comments

  1. adacosta – Posted Oct. 20, 2009

    broken paragraph

  2. cnk – Posted April 9, 2011

    Why are there no comment links next to the sections below?

    I would like to comment on the change_table example. The footnote is great. But your example could use a comment making it clear that you added a column and an index on that column. It might also be nice to show removing a column to demonstrate a larger range of changes.

Add a comment

View 1 comment

  1. cnk – Posted April 9, 2011

    Very nice explanation. I would suggest removing earlier references to owners, etc. and use the text in this section to introduce Associations for the first time.

Add a comment

View 1 comment

  1. suhair – Posted Oct. 4, 2010

    This method returns an array-like object which gives access to all the student records which have the school's id in their school_id column. Here have to replace with person and their puppies

Add a comment

View 1 comment

  1. suhair – Posted Oct. 4, 2010

    Concept of puppies and students messed up here.

Add a comment

View 1 comment

  1. espinet – Posted June 11, 2010

    maybe give this a bit more of an explanation? i could be like, but why wouldnt I use the CamelCase name for the association.

Add a comment

View 1 comment

  1. cnk – Posted April 9, 2011

    I would suggest showing an example where the save! fails to demo the exception that is raised.

    This would also be a good place to show how to get to the errors array - or at least provide a reference to the API docs for accessing validation errors.

Add a comment

View 1 comment

  1. davesailer – Posted Oct. 22, 2009

    Singular subject/plural verb: "There are a number of ways"

    Think: "If here are a number of ways, then what are that number?"

    Fix could be: "There is a number of ways" or "There are ways" or "There are many ways"

Add a comment

View 1 comment

  1. Shripad – Posted Aug. 26, 2010

    Cloud.where(:looks_like => ['elephant', 'mouse']) instead of Cloud.where(:looks_like => ['elephant', 'mouse']).all

Add a comment

View 4 comments

  1. cyberkni – Posted Oct. 20, 2009

    I don't see anything about the dynamic finders here. They are really useful when you're trying to make your code expressive.

    It doesn't need to be a big section, but a single example of Cloud.find_all_by_type('cumulus') would be useful in giving people a kick in the right direction.

  2. MikeSummers – Posted Oct. 21, 2009

    STI? HABTM? Polymorphic associations?

  3. bitzesty – Posted Oct. 26, 2009

    named scopes

  4. cnk – Posted April 9, 2011

    In the section below, the example for only and except looks like it is missing the output from asking for the SQL that is produced.

Add a comment

View 1 comment

  1. cnk – Posted April 9, 2011

    It might be useful to reitterate that many of these specialty methods do not perform validations before doing the save - and then reference the section earlier in the chapter. Given how commonly it is asserted that AR validations are universally enforced, it is important to repeat that there are loopholes.

Add a comment