4.2. Creating Database/Model

Buy the new Rails 5.1 version of this book.

Model in this context refers to the data model of Model-View-Controller (MVC).
As a first example, let's take a list of countries in Europe. First, we create a new Rails project:
$ rails new europe
  [...]
$ cd europe
$
Next, let's have a look at the help page for rails generate model:
$ rails generate model
Usage:
  rails generate model NAME [field[:type][:index] field[:type][:index]] [options]

Options:
      [--skip-namespace]  # Skip namespace (affects only isolated applications)
  -o, --orm=NAME          # Orm to be invoked
                          # Default: active_record

ActiveRecord options:
      [--migration]            # Indicates when to generate migration
                               # Default: true
      [--timestamps]           # Indicates when to generate timestamps
                               # Default: true
      [--parent=PARENT]        # The parent class for the generated model
      [--indexes]              # Add indexes for references and belongs_to columns
                               # Default: true
  -t, [--test-framework=NAME]  # Test framework to be invoked
                               # Default: test_unit

TestUnit options:
      [--fixture]                   # Indicates when to generate fixture
                                    # Default: true
  -r, [--fixture-replacement=NAME]  # Fixture replacement to be invoked

Runtime options:
  -f, [--force]    # Overwrite files that already exist
  -p, [--pretend]  # Run but do not make any changes
  -q, [--quiet]    # Suppress status output
  -s, [--skip]     # Skip files that already exist

Description:
    Stubs out a new model. Pass the model name, either CamelCased or
    under_scored, and an optional list of attribute pairs as arguments.

    Attribute pairs are field:type arguments specifying the
    model's attributes. Timestamps are added by default, so you don't have to
    specify them by hand as 'created_at:datetime updated_at:datetime'.

    You don't have to think up every attribute up front, but it helps to
    sketch out a few so you can start working with the model immediately.

    This generator invokes your configured ORM and test framework, which
    defaults to ActiveRecord and TestUnit.

    Finally, if --parent option is given, it's used as superclass of the
    created model. This allows you create Single Table Inheritance models.

    If you pass a namespaced model name (e.g. admin/account or Admin::Account)
    then the generator will create a module with a table_name_prefix method
    to prefix the model's table name with the module name (e.g. admin_account)

Available field types:

    Just after the field name you can specify a type like text or boolean.
    It will generate the column with the associated SQL type. For instance:

        `rails generate model post title:string body:text`

    will generate a title column with a varchar type and a body column with a text
    type. You can use the following types:

        integer
        primary_key
        decimal
        float
        boolean
        binary
        string
        text
        date
        time
        datetime
        timestamp

    You can also consider `references` as a kind of type. For instance, if you run:

        `rails generate model photo title:string album:references`

    It will generate an album_id column. You should generate this kind of fields when
    you will use a `belongs_to` association for instance. `references` also support
    the polymorphism, you could enable the polymorphism like this:

        `rails generate model product supplier:references{polymorphic}`

    For integer, string, text and binary fields an integer in curly braces will
    be set as the limit:

        `rails generate model user pseudo:string{30}`

    For decimal two integers separated by a comma in curly braces will be used
    for precision and scale:

        `rails generate model product price:decimal{10,2}`

    You can add a `:uniq` or `:index` suffix for unique or standard indexes
    respectively:

        `rails generate model user pseudo:string:uniq`
        `rails generate model user pseudo:string:index`

    You can combine any single curly brace option with the index options:

        `rails generate model user username:string{30}:uniq`
        `rails generate model product supplier:references{polymorphic}:index`


Examples:
    `rails generate model account`

        For ActiveRecord and TestUnit it creates:

            Model:      app/models/account.rb
            Test:       test/models/account_test.rb
            Fixtures:   test/fixtures/accounts.yml
            Migration:  db/migrate/XXX_create_accounts.rb

    `rails generate model post title:string body:text published:boolean`

        Creates a Post model with a string title, text body, and published flag.

    `rails generate model admin/account`

        For ActiveRecord and TestUnit it creates:

            Module:     app/models/admin.rb
            Model:      app/models/admin/account.rb
            Test:       test/models/admin/account_test.rb
            Fixtures:   test/fixtures/admin/accounts.yml
            Migration:  db/migrate/XXX_create_admin_accounts.rb

$
The usage description rails generate model NAME [field[:type][:index] field[:type][:index]] [options] tells us that after rails generate model comes the name of the model and then the table fields. If you do not put :type after a table field name, it is assumed to be a string by default. Let's create the model country:
$ rails generate model Country name population:integer
      invoke  active_record
      create    db/migrate/20130715174248_create_countries.rb
      create    app/models/country.rb
      invoke    test_unit
      create      test/models/country_test.rb
      create      test/fixtures/countries.yml
$
The generator has created a database migration file with the name db/migrate/20130715174248_create_countries.rb. It provides the following code:
class CreateCountries < ActiveRecord::Migration
  def change
    create_table :countries do |t|
      t.string :name
      t.integer :population

      t.timestamps
    end
  end
end
A migration contains database changes. In this migration, a class CreateCountries is defined as a child of ActiveRecord::Migration. The class method change is used to define a migration and the associated roll-back.
With rake db:migrate we can apply the migrations, in other words, create the corresponding database table:
$ rake db:migrate
==  CreateCountries: migrating ================================================
-- create_table(:countries)
   -> 0.0010s
==  CreateCountries: migrated (0.0011s) =======================================

$ 

Buy the new Rails 5.1 version of this book.

You will find more details on migrations in Section 4.16, “Migrations”.
Let's have a look at the file app/models/country.rb:
class Country < ActiveRecord::Base
end
Hmmm … the class Country is a child of ActiveRecord::Base. Makes sense, as we are discussing ActiveRecord in this chapter. ;-)

The Attributes id, created_at and updated_at

Even if you cannot see it in the migration, we also get the attributes id, created_at und updated_at by default for each ActiveRecord model. In the Rails console, we can output the attributes of the class Country by entering the class name:
$ rails console
Loading development environment (Rails 4.0.0)
>> Country
=> Country(id: integer, name: string, population: integer, created_at: datetime, updated_at: datetime)
>> exit
$
The attribute created_at stores the time when the record was initially created. updated_at shows the time of the last update for this record.
id is used a central identification of the record (primary key). The id is automatically incremented by 1 for each record.

Getters and Setters

To read and write values of a SQL table row you can use by ActiveRecord provided getters and setters (the section called “Getters and Setters”). These attr_accessors are automatically created. The getter of the field updated_at for a given Country with the name germany would be germany.updated_at.

Possible Data Types in ActiveRecord

ActiveRecord is a layer between Ruby and various relational databases. Unfortunately, many SQL databases have different perspectives regarding the definition of columns and their content. But you do not need to worry about this, because ActiveRecord solves this problem transparently for you.
  • Advantage:
    We can replace the database behind a Rails application without having to touch the program code.
  • Disadvantage:
    We cannot use all the features of the database concerned. We have to use the least common denominator, so to speak.
To generate a model, you can use the following field types:
  • binary
    This is a BLOB (Binary Large Object) in the classical sense. Never heard of it? Then you probably won't need it.
  • boolean
    A Boolean value. Can be either true or false.
  • date
    You can store a date here.
  • datetime
    Here you can store a date including a time.
  • float
    For storing a floating point number.
  • integer
    For storing an integer.
  • decimal
    For storing a decimal number.

    Buy the new Rails 5.1 version of this book.

    You can also enter a decimal directly with the model generator. But you need to observe the special syntax. Example for creating a price with a decimal:
    $ rails generate model product name 'price:decimal{7,2}'
          invoke  active_record
          create    db/migrate/20121114110808_create_products.rb
          create    app/models/product.rb
          invoke    test_unit
          create      test/unit/product_test.rb
          create      test/fixtures/products.yml
    $
    That would generate the following migration (db/migrate/20121114110808_create_products.rb):
    class CreateProducts < ActiveRecord::Migration
      def change
        create_table :products do |t|
          t.string :name
          t.decimal :price, :precision => 7, :scale => 2
    
          t.timestamps
        end
      end
    end
  • primary_key
    This is an integer that is automatically incremented by 1 by the database for each new entry. This field type is often used as key for linking different database tables or models.
  • string
    A string, in other words a sequence of any characters, up to a maximum of 28-1 (= 255) characters.
  • text
    Also a string - but considerably bigger. By default, up to 216 (= 65536) characters can be saved here.
  • time
    A time.
  • timestamp
    A time with date, filled in automatically by the database.
In Section 4.16, “Migrations” we will provide more information on the individual data types and discuss available options. Don't forget, this is a book for beginners, so this section just gives a brief overview. If you want to find out more about the various datatypes, please refer to the documentation listed in Appendix A, Further Rails Documentation.

Naming Conventions (Country vs. country vs. countries)

Rails newbies often find it hard to figure out when to use upper and lower case, for example, Country or country (one is a class, the other one a model). The problem is usually not the class itself, but purely the spelling or wording. For now, let's just say: it's all very logical and you will quickly get the hang of it. The important thing is that you keep using English words, even if you would normally be programming in another language (see the section called “Why Is It All in English?”).
Originally, my plan was to now start philosophizing at great length on naming conventions. But then I thought: Jeez, the readers want to get going and not sit here for ages reading about theory. So I am now going to introduce the methods with which you can find out the naming conventions yourself in the Rails console:
$ rails console
Loading development environment (Rails 4.0.0)
>> 'country'.classify
=> "Country"
>> 'country'.tableize
=> "countries"
>> 'country'.foreign_key
=> "country_id"
>>
ActiveRecord automatically uses the English plural forms. So for the class Country, it's countries. If you are not sure about a term, you can also work with the class and method name.
>> Country.name.tableize
=> "countries"
>> Country.name.foreign_key
=> "country_id"
>> exit
$ 
You will find a complete list of the corresponding methods at http://rails.rubyonrails.org/classes/ActiveSupport/CoreExtensions/String/Inflections.html. But I would recommend that, for now, you just go with the flow. If you are not sure, you can find out the correct notation with the methods shown above.

Database Configuration

Which database is used by default? Let's have a quick look at the configuration file for the database (config/database.yml):
# 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
As we are working in development mode, Rails has created a new SQLite3 database db/development.sqlite3 as a result of rake db:migrate and saved all data there.
Fans of command line clients can use sqlite3 for viewing this database:
$ sqlite3 db/development.sqlite3 
SQLite version 3.7.12 2012-04-03 19:43:07
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .tables
countries          schema_migrations
sqlite> .schema countries
CREATE TABLE "countries" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "population" integer, "created_at" datetime, "updated_at" datetime);
sqlite> .exit
$