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
endThis 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.rbWe 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_columnadd_indexadd_timestampscreate_tableremove_timestampsrename_columnrename_indexrename_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 Type | SQLite Type | Ruby Class |
|---|---|---|
| integer | integer | Fixnum |
| decimal | decimal | BigDecimal |
| float | float | Float |
| string | varchar(255) | String |
| text | text | String |
| binary | blob | String |
| date | date | Date |
| datetime | datetime | Time |
| timestamp | datetime | Time |
| time | time | Time |
| boolean | boolean | TrueClass/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:
:idSet to
falseif you don't want this table to get the default primary key id column.:primary_keyUnless you've set
:idtofalse, this option specifies the name of the automatically added primary key column.:optionsThis 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".:temporarySet to
trueif you are creating a temporary table.:forceSet to
trueif you want any existing table of the same name to be dropped when this table is created. Defaults tofalse, 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:
:limitThis option specifies the number of bytes used to store values of this column.
:defaultThe default value of this column for new database records and new Active Record instances.
:nullSet this option to
falseto disallowNULLvalues in this column. The default for this option istrue, which allowsNULLvalues.
In addition to these general-purpose options, there are two options specific to decimal columns:
:precisionSpecifies the number of significant digits in a decimal column. A value of 123.45 has a precision of 5.
:scaleThis 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:
ownerReturns a
Personobject representing the associated person record in the database; that is, the person with an id equal to the owner_id attribute in thePuppyobject. Returnsnilif no record is found.owner=(person)Assigns the given
Personobject to thePuppyobject’s association and sets the owner_id of the puppy record to the id of the given person.build_owner(attributes)Initializes a new
Personobject with the given attributes and assigns it to thePuppy, 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
:autosaveIf
:autosaveis 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 themark_for_destructionmethod called on them will be destroyed. Defaults tofalse.:class_nameThis 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
:conditionsSets 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
createandbuildmethods 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'} endIn this case,
student.canadian_addresseswould only return the student's addresses in Canada, andstudent.canadian_addresses.buildwould initialize aMailingAddresswith country already set to "CA".:dependentDetermines what to do with associated objects when instances of this class are destroyed.
The
:destroysetting calls thedestroymethod on each associated object when the parent is destroyed.When set to
:delete(for abelongs_toorhas_oneassociation) or:delete_all(forhas_manyassociations), the associated records are deleted from the database without using thedestroymethod. This means that no callbacks are run before deletion (such as from a:dependentoption 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_oneandhas_manyassociations,:dependent => :nullifysimply sets the relevant foreign keys of the associated records toNULL.
By default, the associated objects are left alone.
:foreign_keySpecifies the name of the foreign key used for this association. For
has_oneandhas_manyassociations, this defaults to the singular underscored name of the current class with"_id"appended. Forbelongs_toassociations, 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:includeSpecifies second-order associations which are eager-loaded with this association in the same manner as Active Record's finder methods.
:readonlyWhen
:readonlyis true, objects loaded through this association cannot be saved.:selectTakes 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.
:validateIf 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 => /@/
endYou 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']}
endformat
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 => /@/}
endinclusion
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"
# }
endlength/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}
endIn 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"
# }
endere 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}
endHere, 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
endWhen 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!touchupdate_attributeupdate_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_counterincrement_counterupdate_allupdate_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
endAnother 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
endThe 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
saveoperation, but it also happens in other circumstances such as when you explicitly callvalid?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_sqlYou can also build relations off of has_many associations:
kathy.puppies.where(:demeanor => "vigilant").to_sql # => "SELECT ..."
If you've
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_attributeupdate_attributesupdate_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
endEvery 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
endNotice 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.






Add a comment



Add a comment