Rails in a nutshell Chapter 3. Active Record

Translations of this material:

into Russian: Rails in a nutshell Глава 3. Active Record. 30% translated in draft.
Submitted for translation by asplogika 01.11.2009 Published 2 years, 1 month ago.

Text

Chapter 3. Active Record

Connecting to a Database

Before Active Record can work any of its 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 involves a small configuration file located at 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 how the untouched database.yml looks:

# SQLite version 3.x

# gem install sqlite3-ruby (not necessary on OS X Leopard)

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-ruby gem installed before things can 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 and is discussed in ???.

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

$ rails 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. The options for each adapter's configuration are detailed in ???.

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 withe 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:

1 comment

1.

Alan Da Costa Posted 1 day and 3 hours ago

* "to operate on" => "to operate in"

* sp "withe" => "with the" ; maybe consider replacing with, "in the"

$ 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. For example you might find it most convenient to use SQLite3 for development, but want some features of PostgreSQL for your live application. 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

Part of Rails' convention-over-configuration approach involves naming things in unsurprising ways so that the framework doesn't need to be told how to find things. Each ActiveRecord model in a Rails application is a subclass of ActiveRecord::Base, and it should follow the ruby convention of having capitalized CamelCase class names. The model's filename should be like the class name, but all in underscored lowercase. So 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 is like the file's base name but also pluralized. School records live in the schools table; CourseEnrollment records live in the course_enrollments table.

Developing your Database Schema with Migrations

After your application has a database to work with, you can start populating it with the tables and columns that define the foundation of your data model: the 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 migrations 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 what we get when we run the migration generator:

$ script/generate migration CreateUsersAndClouds

create db/migrate

create db/migrate/20090610033819_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 self.up

end

def self.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 is going define how those changes can be undone. The application we're creating here is going to allow people to contribute URLs to images of clouds on the web, and to enter

1 comment

1.

Alan Da Costa Posted 1 day and 3 hours ago

broken paragraph

Here, we want to create a table for users of the application and

1 comment

1.

Alan Da Costa Posted 1 day and 3 hours ago

broken paragraph

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 School object might have many Student objects associated with it, with the School keeping track of its Students by storing references to them in a collection object of some kind. In a relational database, however, it is the child entities which keep track of the parents: each row in the students table would reference an associated row in the schools table by storing its school's primary key in a special column in the students 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 school's students can be completed very quickly without the database having to look at each student row one at a time.

This is a very good thing, but it can be very unintuitive for object-oriented programmers to suddenly have to think about relationships in this way. Wouldn't it be nice if we could just type out school.students and get back what we expect? Yes it would, and here's how you do it with ActiveRecord:

class School < ActiveRecord::Base

has_many :students

end

Besides the schools table itself, all this requires is a students table with a column called school_id. The has_many method is a class method on ActiveRecord::Base which, among other things, dynamically defines a public instance method on School called students. 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.

Note

The object returned by the students method looks a whole lot like a standard Ruby Array:

school = School.first

# => #<School id: 1, name: "Lisgar Collegiate Institute">

school.students[0..1].map(&:name)

# => ["Jimmy", "Jane"]

school.students.class

# => Array

But beware: even while that object tells you with a straight face that it's an Array, it is lying to you. Most of the time you can treat it like an Array and you wouldn't notice the difference, but really it is an instance of ActiveRecord::Associations::AssociationProxy with a lot more smarts behind it than a simple Array. It's good to keep this in mind when that association object doesn't behave quite the way you expect it to.

By calling the has_many method in the School 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 school-student relationship, we would make a similar declaration in the Student class:

class Student < ActiveRecord::Base

belongs_to :school

end

Note

Associations don't really need to be reciprocated; that is, a has_many declaration does not need a corresponding belongs_to in the associated model in order to function correctly. It usually makes things easier if you declare both sides of an association, though, especially when other developers are exploring your code.

belongs_to

The belongs_to association is defined on the model with the foreign key column in its table. 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, the Student class would get the following instance methods when its belongs_to class method is called with :school:

school

Returns a School object representing the associated school record in the database; that is, the school with an id equal to the school_id attribute in the Student object. Returns nil if no record is found.

school=(new_school)

Assigns the given School object to the Student object's association and sets the school_id of the student to the id of the school.

build_school(attributes)

Initializes a new School object with the given attributes and assigns it to the Student, but does not save the newly built object. Since the unsaved school doesn't yet have a primary key assigned from the database, the student's school_id is left untouched until both halves of the association are saved.

create_school(attributes)

Like build_school, but saves both objects in the association.

has_one

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

class Student < ActiveRecord::Base

belongs_to :school

has_one :mailing_address

Pages: ← previous Ctrl next
1 2 3

© CC. License: CC