Migrations

Create Tables

A table is defined via #create_table. This method accepts two arguments: the name and a block that expresses the design.

Safe operation can be performed via #create_table?. It only creates the table if it doesn't exist. Force operation can be performed via #create_table!. It drops the existing table and creates a new one from scratch. These operations shouldn't be used in migrations.

Column Definition

To define a column we use #column, followed by the name, the type and options. The name must be a unique identifier within the table.

The type can be a Ruby type (e.g. String), a symbol that represents a Ruby type (e.g. :string) , or a string that represents the raw database type (e.g. "varchar(255)").

Type Definition

The following Ruby types are supported:

Their translation from Ruby types to database types may vary from database to database.

Options

It supports the following options:

Primary Key

We can define primary keys with the following syntaxes:

column :id, Integer, null: false, primary_key: true
# or just use this shortcut
primary_key :id

Foreign Keys

Foreign keys are defined via #foreign_key, where we specify the name of the column, the referenced table, and a set of options. The following example creates an author_id column (integer) for books and adds a foreign key.

create_table :books do
  # ...
  foreign_key :author_id, :authors, on_delete: :cascade, null: false
end
Options

It accepts the following options:

Indexes

Indexes are defined via #index. It accepts the name(s) of the column(s), and a set of options.

create_table :stores do
  # ...
  column :code, Integer, null: false
  column :lat, Float
  column :lng, Float

  index :code, unique: true
  index [:lat, :lng], name: :stores_coords_index
end
Options

It accepts the following options:

Constraints

We can define constraints on columns via #constraint. It accepts a name and a block.

create_table :users do
  # ...
  column :age, Integer
  constraint(:adult_constraint) { age > 18 }
end

Please note that the block is evaluated in the context of the database engine, complex Ruby code doesn't work. Database functions are mapped to Ruby functions, but this reduces the portability of the migration.

create_table :users do
  # ...
  column :password, String
  constraint(:password_length_constraint) { char_length(password) >= 8 }
end

Checks

Checks are similar to constraints, but they accept an anonymous block or a SQL raw string.

create_table :users do
  # ...
  column :age, Integer
  column :role, String

  check { age > 18 }
  check %(role IN("contributor", "manager", "owner"))
end

Prev: Migrations - Overview
Next: Migrations - Alter Table
Looking for Lotus? We renamed the project and it's now called Hanami. Read the announcement.