diff options
Diffstat (limited to 'guides/source/migrations.textile')
-rw-r--r-- | guides/source/migrations.textile | 932 |
1 files changed, 932 insertions, 0 deletions
diff --git a/guides/source/migrations.textile b/guides/source/migrations.textile new file mode 100644 index 0000000000..52dba76e68 --- /dev/null +++ b/guides/source/migrations.textile @@ -0,0 +1,932 @@ +h2. Migrations + +Migrations are a convenient way for you to alter your database in a structured +and organized manner. You could edit fragments of SQL by hand but you would then +be responsible for telling other developers that they need to go and run them. +You'd also have to keep track of which changes need to be run against the +production machines next time you deploy. + +Active Record tracks which migrations have already been run so all you have to +do is update your source and run +rake db:migrate+. Active Record will work out +which migrations should be run. It will also update your +db/schema.rb+ file to +match the structure of your database. + +Migrations also allow you to describe these transformations using Ruby. The +great thing about this is that (like most of Active Record's functionality) it +is database independent: you don't need to worry about the precise syntax of ++CREATE TABLE+ any more than you worry about variations on +SELECT *+ (you can +drop down to raw SQL for database specific features). For example you could use +SQLite3 in development, but MySQL in production. + +In this guide, you'll learn all about migrations including: + +* The generators you can use to create them +* The methods Active Record provides to manipulate your database +* The Rake tasks that manipulate them +* How they relate to +schema.rb+ + +endprologue. + +h3. Anatomy of a Migration + +Before we dive into the details of a migration, here are a few examples of the +sorts of things you can do: + +<ruby> +class CreateProducts < ActiveRecord::Migration + def up + create_table :products do |t| + t.string :name + t.text :description + + t.timestamps + end + end + + def down + drop_table :products + end +end +</ruby> + +This migration adds a table called +products+ with a string column called +name+ +and a text column called +description+. A primary key column called +id+ will +also be added, however since this is the default we do not need to explicitly specify it. +The timestamp columns +created_at+ and +updated_at+ which Active Record +populates automatically will also be added. Reversing this migration is as +simple as dropping the table. + +Migrations are not limited to changing the schema. You can also use them to fix +bad data in the database or populate new fields: + +<ruby> +class AddReceiveNewsletterToUsers < ActiveRecord::Migration + def up + change_table :users do |t| + t.boolean :receive_newsletter, :default => false + end + User.update_all :receive_newsletter => true + end + + def down + remove_column :users, :receive_newsletter + end +end +</ruby> + +NOTE: Some "caveats":#using-models-in-your-migrations apply to using models in +your migrations. + +This migration adds a +receive_newsletter+ column to the +users+ table. We want +it to default to +false+ for new users, but existing users are considered to +have already opted in, so we use the User model to set the flag to +true+ for +existing users. + +h4. Using the change method + +Rails 3.1 makes migrations smarter by providing a new <tt>change</tt> method. +This method is preferred for writing constructive migrations (adding columns or +tables). The migration knows how to migrate your database and reverse it when +the migration is rolled back without the need to write a separate +down+ method. + +<ruby> +class CreateProducts < ActiveRecord::Migration + def change + create_table :products do |t| + t.string :name + t.text :description + + t.timestamps + end + end +end +</ruby> + +h4. Migrations are Classes + +A migration is a subclass of <tt>ActiveRecord::Migration</tt> that implements +two methods: +up+ (perform the required transformations) and +down+ (revert +them). + +Active Record provides methods that perform common data definition tasks in a +database independent way (you'll read about them in detail later): + +* +add_column+ +* +add_index+ +* +change_column+ +* +change_table+ +* +create_table+ +* +create_join_table+ +* +drop_table+ +* +remove_column+ +* +remove_index+ +* +rename_column+ + +If you need to perform tasks specific to your database (for example create a +"foreign key":#active-record-and-referential-integrity constraint) then the ++execute+ method allows you to execute arbitrary SQL. A migration is just a +regular Ruby class so you're not limited to these functions. For example after +adding a column you could write code to set the value of that column for +existing records (if necessary using your models). + +On databases that support transactions with statements that change the schema +(such as PostgreSQL or SQLite3), migrations are wrapped in a transaction. If the +database does not support this (for example MySQL) then when a migration fails +the parts of it that succeeded will not be rolled back. You will have to rollback +the changes that were made by hand. + +h4. What's in a Name + +Migrations are stored as files in the +db/migrate+ directory, one for each +migration class. The name of the file is of the form ++YYYYMMDDHHMMSS_create_products.rb+, that is to say a UTC timestamp +identifying the migration followed by an underscore followed by the name +of the migration. The name of the migration class (CamelCased version) +should match the latter part of the file name. For example ++20080906120000_create_products.rb+ should define class +CreateProducts+ and ++20080906120001_add_details_to_products.rb+ should define ++AddDetailsToProducts+. If you do feel the need to change the file name then you +<em>have to</em> update the name of the class inside or Rails will complain +about a missing class. + +Internally Rails only uses the migration's number (the timestamp) to identify +them. Prior to Rails 2.1 the migration number started at 1 and was incremented +each time a migration was generated. With multiple developers it was easy for +these to clash requiring you to rollback migrations and renumber them. With +Rails 2.1+ this is largely avoided by using the creation time of the migration +to identify them. You can revert to the old numbering scheme by adding the +following line to +config/application.rb+. + +<ruby> +config.active_record.timestamped_migrations = false +</ruby> + +The combination of timestamps and recording which migrations have been run +allows Rails to handle common situations that occur with multiple developers. + +For example Alice adds migrations +20080906120000+ and +20080906123000+ and Bob +adds +20080906124500+ and runs it. Alice finishes her changes and checks in her +migrations and Bob pulls down the latest changes. When Bob runs +rake db:migrate+, +Rails knows that it has not run Alice's two migrations so it executes the +up+ method for each migration. + +Of course this is no substitution for communication within the team. For +example, if Alice's migration removed a table that Bob's migration assumed to +exist, then trouble would certainly strike. + +h4. Changing Migrations + +Occasionally you will make a mistake when writing a migration. If you have +already run the migration then you cannot just edit the migration and run the +migration again: Rails thinks it has already run the migration and so will do +nothing when you run +rake db:migrate+. You must rollback the migration (for +example with +rake db:rollback+), edit your migration and then run +rake db:migrate+ to run the corrected version. + +In general editing existing migrations is not a good idea: you will be creating +extra work for yourself and your co-workers and cause major headaches if the +existing version of the migration has already been run on production machines. +Instead, you should write a new migration that performs the changes you require. +Editing a freshly generated migration that has not yet been committed to source +control (or, more generally, which has not been propagated beyond your +development machine) is relatively harmless. + +h4. Supported Types + +Active Record supports the following database column types: + +* +:binary+ +* +:boolean+ +* +:date+ +* +:datetime+ +* +:decimal+ +* +:float+ +* +:integer+ +* +:primary_key+ +* +:string+ +* +:text+ +* +:time+ +* +:timestamp+ + +These will be mapped onto an appropriate underlying database type. For example, +with MySQL the type +:string+ is mapped to +VARCHAR(255)+. You can create +columns of types not supported by Active Record when using the non-sexy syntax, +for example + +<ruby> +create_table :products do |t| + t.column :name, 'polygon', :null => false +end +</ruby> + +This may however hinder portability to other databases. + +h3. Creating a Migration + +h4. Creating a Model + +The model and scaffold generators will create migrations appropriate for adding +a new model. This migration will already contain instructions for creating the +relevant table. If you tell Rails what columns you want, then statements for +adding these columns will also be created. For example, running + +<shell> +$ rails generate model Product name:string description:text +</shell> + +will create a migration that looks like this + +<ruby> +class CreateProducts < ActiveRecord::Migration + def change + create_table :products do |t| + t.string :name + t.text :description + + t.timestamps + end + end +end +</ruby> + +You can append as many column name/type pairs as you want. By default, the +generated migration will include +t.timestamps+ (which creates the ++updated_at+ and +created_at+ columns that are automatically populated +by Active Record). + +h4. Creating a Standalone Migration + +If you are creating migrations for other purposes (for example to add a column +to an existing table) then you can also use the migration generator: + +<shell> +$ rails generate migration AddPartNumberToProducts +</shell> + +This will create an empty but appropriately named migration: + +<ruby> +class AddPartNumberToProducts < ActiveRecord::Migration + def change + end +end +</ruby> + +If the migration name is of the form "AddXXXToYYY" or "RemoveXXXFromYYY" and is +followed by a list of column names and types then a migration containing the +appropriate +add_column+ and +remove_column+ statements will be created. + +<shell> +$ rails generate migration AddPartNumberToProducts part_number:string +</shell> + +will generate + +<ruby> +class AddPartNumberToProducts < ActiveRecord::Migration + def change + add_column :products, :part_number, :string + end +end +</ruby> + +Similarly, + +<shell> +$ rails generate migration RemovePartNumberFromProducts part_number:string +</shell> + +generates + +<ruby> +class RemovePartNumberFromProducts < ActiveRecord::Migration + def up + remove_column :products, :part_number + end + + def down + add_column :products, :part_number, :string + end +end +</ruby> + +You are not limited to one magically generated column, for example + +<shell> +$ rails generate migration AddDetailsToProducts part_number:string price:decimal +</shell> + +generates + +<ruby> +class AddDetailsToProducts < ActiveRecord::Migration + def change + add_column :products, :part_number, :string + add_column :products, :price, :decimal + end +end +</ruby> + +As always, what has been generated for you is just a starting point. You can add +or remove from it as you see fit by editing the +db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rb file. + +NOTE: The generated migration file for destructive migrations will still be +old-style using the +up+ and +down+ methods. This is because Rails needs to know +the original data types defined when you made the original changes. + +h3. Writing a Migration + +Once you have created your migration using one of the generators it's time to +get to work! + +h4. Creating a Table + +Migration method +create_table+ will be one of your workhorses. A typical use +would be + +<ruby> +create_table :products do |t| + t.string :name +end +</ruby> + +which creates a +products+ table with a column called +name+ (and as discussed +below, an implicit +id+ column). + +The object yielded to the block allows you to create columns on the table. There +are two ways of doing it. The first (traditional) form looks like + +<ruby> +create_table :products do |t| + t.column :name, :string, :null => false +end +</ruby> + +The second form, the so called "sexy" migration, drops the somewhat redundant ++column+ method. Instead, the +string+, +integer+, etc. methods create a column +of that type. Subsequent parameters are the same. + +<ruby> +create_table :products do |t| + t.string :name, :null => false +end +</ruby> + +By default, +create_table+ will create a primary key called +id+. You can change +the name of the primary key with the +:primary_key+ option (don't forget to +update the corresponding model) or, if you don't want a primary key at all (for +example for a HABTM join table), you can pass the option +:id => false+. If you +need to pass database specific options you can place an SQL fragment in the ++:options+ option. For example, + +<ruby> +create_table :products, :options => "ENGINE=BLACKHOLE" do |t| + t.string :name, :null => false +end +</ruby> + +will append +ENGINE=BLACKHOLE+ to the SQL statement used to create the table +(when using MySQL, the default is +ENGINE=InnoDB+). + +h4. Creating a Join Table + +Migration method +create_join_table+ creates a HABTM join table. A typical use +would be + +<ruby> +create_join_table :products, :categories +</ruby> + +which creates a +categories_products+ table with two columns called +category_id+ and +product_id+. +These columns have the option +:null+ set to +false+ by default. + +You can pass the option +:table_name+ with you want to customize the table name. For example, + +<ruby> +create_join_table :products, :categories, :table_name => :categorization +</ruby> + +will create a +categorization+ table. + +By default, +create_join_table+ will create two columns with no options, but you can specify these +options using the +:column_options+ option. For example, + +<ruby> +create_join_table :products, :categories, :column_options => {:null => true} +</ruby> + +will create the +product_id+ and +category_id+ with the +:null+ option as +true+. + +h4. Changing Tables + +A close cousin of +create_table+ is +change_table+, used for changing existing +tables. It is used in a similar fashion to +create_table+ but the object yielded +to the block knows more tricks. For example + +<ruby> +change_table :products do |t| + t.remove :description, :name + t.string :part_number + t.index :part_number + t.rename :upccode, :upc_code +end +</ruby> + +removes the +description+ and +name+ columns, creates a +part_number+ string +column and adds an index on it. Finally it renames the +upccode+ column. + +h4. Special Helpers + +Active Record provides some shortcuts for common functionality. It is for +example very common to add both the +created_at+ and +updated_at+ columns and so +there is a method that does exactly that: + +<ruby> +create_table :products do |t| + t.timestamps +end +</ruby> + +will create a new products table with those two columns (plus the +id+ column) +whereas + +<ruby> +change_table :products do |t| + t.timestamps +end +</ruby> +adds those columns to an existing table. + +Another helper is called +references+ (also available as +belongs_to+). In its +simplest form it just adds some readability. + +<ruby> +create_table :products do |t| + t.references :category +end +</ruby> + +will create a +category_id+ column of the appropriate type. Note that you pass +the model name, not the column name. Active Record adds the +_id+ for you. If +you have polymorphic +belongs_to+ associations then +references+ will add both +of the columns required: + +<ruby> +create_table :products do |t| + t.references :attachment, :polymorphic => {:default => 'Photo'} +end +</ruby> + +will add an +attachment_id+ column and a string +attachment_type+ column with +a default value of 'Photo'. +references+ also allows you to define an +index directly, instead of using +add_index+ after the +create_table+ call: + +<ruby> +create_table :products do |t| + t.references :category, :index => true +end +</ruby> + +will create an index identical to calling `add_index :products, :category_id`. + +NOTE: The +references+ helper does not actually create foreign key constraints +for you. You will need to use +execute+ or a plugin that adds "foreign key +support":#active-record-and-referential-integrity. + +If the helpers provided by Active Record aren't enough you can use the +execute+ +method to execute arbitrary SQL. + +For more details and examples of individual methods, check the API documentation, +in particular the documentation for +"<tt>ActiveRecord::ConnectionAdapters::SchemaStatements</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html +(which provides the methods available in the +up+ and +down+ methods), +"<tt>ActiveRecord::ConnectionAdapters::TableDefinition</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html +(which provides the methods available on the object yielded by +create_table+) +and +"<tt>ActiveRecord::ConnectionAdapters::Table</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html +(which provides the methods available on the object yielded by +change_table+). + +h4. Using the +change+ Method + +The +change+ method removes the need to write both +up+ and +down+ methods in +those cases that Rails knows how to revert the changes automatically. Currently, +the +change+ method supports only these migration definitions: + +* +add_column+ +* +add_index+ +* +add_timestamps+ +* +create_table+ +* +remove_timestamps+ +* +rename_column+ +* +rename_index+ +* +rename_table+ + +If you're going to need to use any other methods, you'll have to write the ++up+ and +down+ methods instead of using the +change+ method. + +h4. Using the +up+/+down+ Methods + +The +down+ method of your migration should revert the transformations done by +the +up+ method. In other words, the database schema should be unchanged if you +do an +up+ followed by a +down+. For example, if you create a table in the +up+ +method, you should drop it in the +down+ method. It is wise to reverse the +transformations in precisely the reverse order they were made in the +up+ +method. For example, + +<ruby> +class ExampleMigration < ActiveRecord::Migration + def up + create_table :products do |t| + t.references :category + end + #add a foreign key + execute <<-SQL + ALTER TABLE products + ADD CONSTRAINT fk_products_categories + FOREIGN KEY (category_id) + REFERENCES categories(id) + SQL + add_column :users, :home_page_url, :string + rename_column :users, :email, :email_address + end + + def down + rename_column :users, :email_address, :email + remove_column :users, :home_page_url + execute <<-SQL + ALTER TABLE products + DROP FOREIGN KEY fk_products_categories + SQL + drop_table :products + end +end +</ruby> + +Sometimes your migration will do something which is just plain irreversible; for +example, it might destroy some data. In such cases, you can raise ++ActiveRecord::IrreversibleMigration+ from your +down+ method. If someone tries +to revert your migration, an error message will be displayed saying that it +can't be done. + +h3. Running Migrations + +Rails provides a set of rake tasks to work with migrations which boil down to +running certain sets of migrations. + +The very first migration related rake task you will use will probably be ++rake db:migrate+. In its most basic form it just runs the +up+ or +change+ +method for all the migrations that have not yet been run. If there are +no such migrations, it exits. It will run these migrations in order based +on the date of the migration. + +Note that running the +db:migrate+ also invokes the +db:schema:dump+ task, which +will update your db/schema.rb file to match the structure of your database. + +If you specify a target version, Active Record will run the required migrations +(up, down or change) until it has reached the specified version. The version +is the numerical prefix on the migration's filename. For example, to migrate +to version 20080906120000 run + +<shell> +$ rake db:migrate VERSION=20080906120000 +</shell> + +If version 20080906120000 is greater than the current version (i.e., it is +migrating upwards), this will run the +up+ method on all migrations up to and +including 20080906120000, and will not execute any later migrations. If +migrating downwards, this will run the +down+ method on all the migrations +down to, but not including, 20080906120000. + +h4. Rolling Back + +A common task is to rollback the last migration, for example if you made a +mistake in it and wish to correct it. Rather than tracking down the version +number associated with the previous migration you can run + +<shell> +$ rake db:rollback +</shell> + +This will run the +down+ method from the latest migration. If you need to undo +several migrations you can provide a +STEP+ parameter: + +<shell> +$ rake db:rollback STEP=3 +</shell> + +will run the +down+ method from the last 3 migrations. + +The +db:migrate:redo+ task is a shortcut for doing a rollback and then migrating +back up again. As with the +db:rollback+ task, you can use the +STEP+ parameter +if you need to go more than one version back, for example + +<shell> +$ rake db:migrate:redo STEP=3 +</shell> + +Neither of these Rake tasks do anything you could not do with +db:migrate+. They +are simply more convenient, since you do not need to explicitly specify the +version to migrate to. + +h4. Resetting the database + +The +rake db:reset+ task will drop the database, recreate it and load the +current schema into it. + +NOTE: This is not the same as running all the migrations - see the section on +"schema.rb":#schema-dumping-and-you. + +h4. Running specific migrations + +If you need to run a specific migration up or down, the +db:migrate:up+ and ++db:migrate:down+ tasks will do that. Just specify the appropriate version and +the corresponding migration will have its +up+ or +down+ method invoked, for +example, + +<shell> +$ rake db:migrate:up VERSION=20080906120000 +</shell> + +will run the +up+ method from the 20080906120000 migration. This task will first +check whether the migration is already performed and will do nothing if Active Record believes +that it has already been run. + +h4. Changing the output of running migrations + +By default migrations tell you exactly what they're doing and how long it took. +A migration creating a table and adding an index might produce output like this + +<shell> +== CreateProducts: migrating ================================================= +-- create_table(:products) + -> 0.0028s +== CreateProducts: migrated (0.0028s) ======================================== +</shell> + +Several methods are provided in migrations that allow you to control all this: + +|_.Method |_.Purpose| +|suppress_messages |Takes a block as an argument and suppresses any output + generated by the block.| +|say |Takes a message argument and outputs it as is. A second + boolean argument can be passed to specify whether to + indent or not.| +|say_with_time |Outputs text along with how long it took to run its + block. If the block returns an integer it assumes it + is the number of rows affected.| + +For example, this migration + +<ruby> +class CreateProducts < ActiveRecord::Migration + def change + suppress_messages do + create_table :products do |t| + t.string :name + t.text :description + t.timestamps + end + end + say "Created a table" + suppress_messages {add_index :products, :name} + say "and an index!", true + say_with_time 'Waiting for a while' do + sleep 10 + 250 + end + end +end +</ruby> + +generates the following output + +<shell> +== CreateProducts: migrating ================================================= +-- Created a table + -> and an index! +-- Waiting for a while + -> 10.0013s + -> 250 rows +== CreateProducts: migrated (10.0054s) ======================================= +</shell> + +If you want Active Record to not output anything, then running +rake db:migrate +VERBOSE=false+ will suppress all output. + +h3. Using Models in Your Migrations + +When creating or updating data in a migration it is often tempting to use one of +your models. After all, they exist to provide easy access to the underlying +data. This can be done, but some caution should be observed. + +For example, problems occur when the model uses database columns which are (1) +not currently in the database and (2) will be created by this or a subsequent +migration. + +Consider this example, where Alice and Bob are working on the same code base +which contains a +Product+ model: + +Bob goes on vacation. + +Alice creates a migration for the +products+ table which adds a new column and +initializes it. She also adds a validation to the +Product+ model for the new +column. + +<ruby> +# db/migrate/20100513121110_add_flag_to_product.rb + +class AddFlagToProduct < ActiveRecord::Migration + def change + add_column :products, :flag, :boolean + Product.update_all :flag => false + end +end +</ruby> + +<ruby> +# app/model/product.rb + +class Product < ActiveRecord::Base + validates :flag, :presence => true +end +</ruby> + +Alice adds a second migration which adds and initializes another column to the ++products+ table and also adds a validation to the +Product+ model for the new +column. + +<ruby> +# db/migrate/20100515121110_add_fuzz_to_product.rb + +class AddFuzzToProduct < ActiveRecord::Migration + def change + add_column :products, :fuzz, :string + Product.update_all :fuzz => 'fuzzy' + end +end +</ruby> + +<ruby> +# app/model/product.rb + +class Product < ActiveRecord::Base + validates :flag, :fuzz, :presence => true +end +</ruby> + +Both migrations work for Alice. + +Bob comes back from vacation and: + +# Updates the source - which contains both migrations and the latest version of +the Product model. +# Runs outstanding migrations with +rake db:migrate+, which +includes the one that updates the +Product+ model. + +The migration crashes because when the model attempts to save, it tries to +validate the second added column, which is not in the database when the _first_ +migration runs: + +<plain> +rake aborted! +An error has occurred, this and all later migrations canceled: + +undefined method `fuzz' for #<Product:0x000001049b14a0> +</plain> + +A fix for this is to create a local model within the migration. This keeps rails +from running the validations, so that the migrations run to completion. + +When using a faux model, it's a good idea to call ++Product.reset_column_information+ to refresh the +ActiveRecord+ cache for the ++Product+ model prior to updating data in the database. + +If Alice had done this instead, there would have been no problem: + +<ruby> +# db/migrate/20100513121110_add_flag_to_product.rb + +class AddFlagToProduct < ActiveRecord::Migration + class Product < ActiveRecord::Base + end + + def change + add_column :products, :flag, :boolean + Product.reset_column_information + Product.update_all :flag => false + end +end +</ruby> + +<ruby> +# db/migrate/20100515121110_add_fuzz_to_product.rb + +class AddFuzzToProduct < ActiveRecord::Migration + class Product < ActiveRecord::Base + end + + def change + add_column :products, :fuzz, :string + Product.reset_column_information + Product.update_all :fuzz => 'fuzzy' + end +end +</ruby> + +h3. Schema Dumping and You + +h4. What are Schema Files for? + +Migrations, mighty as they may be, are not the authoritative source for your +database schema. That role falls to either +db/schema.rb+ or an SQL file which +Active Record generates by examining the database. They are not designed to be +edited, they just represent the current state of the database. + +There is no need (and it is error prone) to deploy a new instance of an app by +replaying the entire migration history. It is much simpler and faster to just +load into the database a description of the current schema. + +For example, this is how the test database is created: the current development +database is dumped (either to +db/schema.rb+ or +db/structure.sql+) and then +loaded into the test database. + +Schema files are also useful if you want a quick look at what attributes an +Active Record object has. This information is not in the model's code and is +frequently spread across several migrations, but the information is nicely +summed up in the schema file. The +"annotate_models":https://github.com/ctran/annotate_models gem automatically +adds and updates comments at the top of each model summarizing the schema if +you desire that functionality. + +h4. Types of Schema Dumps + +There are two ways to dump the schema. This is set in +config/application.rb+ by +the +config.active_record.schema_format+ setting, which may be either +:sql+ or ++:ruby+. + +If +:ruby+ is selected then the schema is stored in +db/schema.rb+. If you look +at this file you'll find that it looks an awful lot like one very big migration: + +<ruby> +ActiveRecord::Schema.define(:version => 20080906171750) do + create_table "authors", :force => true do |t| + t.string "name" + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "products", :force => true do |t| + t.string "name" + t.text "description" + t.datetime "created_at" + t.datetime "updated_at" + t.string "part_number" + end +end +</ruby> + +In many ways this is exactly what it is. This file is created by inspecting the +database and expressing its structure using +create_table+, +add_index+, and so +on. Because this is database-independent, it could be loaded into any database +that Active Record supports. This could be very useful if you were to distribute +an application that is able to run against multiple databases. + +There is however a trade-off: +db/schema.rb+ cannot express database specific +items such as foreign key constraints, triggers, or stored procedures. While in +a migration you can execute custom SQL statements, the schema dumper cannot +reconstitute those statements from the database. If you are using features like +this, then you should set the schema format to +:sql+. + +Instead of using Active Record's schema dumper, the database's structure will be +dumped using a tool specific to the database (via the +db:structure:dump+ Rake task) +into +db/structure.sql+. For example, for the PostgreSQL RDBMS, the ++pg_dump+ utility is used. For MySQL, this file will contain the output of +SHOW +CREATE TABLE+ for the various tables. Loading these schemas is simply a question +of executing the SQL statements they contain. By definition, this will create a +perfect copy of the database's structure. Using the +:sql+ schema format will, +however, prevent loading the schema into a RDBMS other than the one used to +create it. + +h4. Schema Dumps and Source Control + +Because schema dumps are the authoritative source for your database schema, it +is strongly recommended that you check them into source control. + +h3. Active Record and Referential Integrity + +The Active Record way claims that intelligence belongs in your models, not in +the database. As such, features such as triggers or foreign key constraints, +which push some of that intelligence back into the database, are not heavily +used. + +Validations such as +validates :foreign_key, :uniqueness => true+ are one way in +which models can enforce data integrity. The +:dependent+ option on associations +allows models to automatically destroy child objects when the parent is +destroyed. Like anything which operates at the application level, these cannot +guarantee referential integrity and so some people augment them with foreign key +constraints in the database. + +Although Active Record does not provide any tools for working directly with such +features, the +execute+ method can be used to execute arbitrary SQL. You could +also use some plugin like "foreigner":https://github.com/matthuhiggins/foreigner +which add foreign key support to Active Record (including support for dumping +foreign keys in +db/schema.rb+). |