diff options
Diffstat (limited to 'railties/guides/source/migrations.textile')
-rw-r--r-- | railties/guides/source/migrations.textile | 930 |
1 files changed, 0 insertions, 930 deletions
diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile deleted file mode 100644 index c11f8e221b..0000000000 --- a/railties/guides/source/migrations.textile +++ /dev/null @@ -1,930 +0,0 @@ -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 ask for this. -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. - -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'. - -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 know 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. These tasks still -check whether the migration has already run, so for example +db:migrate:up -VERSION=20080906120000+ will do nothing if Active Record believes that -20080906120000 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.all.each do |product| - product.update_attributes!(:flag => 'false') - end - 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.all.each do |product| - product.update_attributes! :fuzz => 'fuzzy' - end - 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 latests 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, :integer - Product.reset_column_information - Product.all.each do |product| - product.update_attributes!(:flag => false) - end - 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.all.each do |product| - product.update_attributes!(:fuzz => 'fuzzy') - end - 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+). |