diff options
Diffstat (limited to 'guides/source/active_record_basics.md')
-rw-r--r-- | guides/source/active_record_basics.md | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md new file mode 100644 index 0000000000..a67e2924d7 --- /dev/null +++ b/guides/source/active_record_basics.md @@ -0,0 +1,393 @@ +**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.** + +Active Record Basics +==================== + +This guide is an introduction to Active Record. + +After reading this guide, you will know: + +* What Object Relational Mapping and Active Record are and how they are used in + Rails. +* How Active Record fits into the Model-View-Controller paradigm. +* How to use Active Record models to manipulate data stored in a relational + database. +* Active Record schema naming conventions. +* The concepts of database migrations, validations, and callbacks. + +-------------------------------------------------------------------------------- + +What is Active Record? +---------------------- + +Active Record is the M in [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) - the +model - which is the layer of the system responsible for representing business +data and logic. Active Record facilitates the creation and use of business +objects whose data requires persistent storage to a database. It is an +implementation of the Active Record pattern which itself is a description of an +Object Relational Mapping system. + +### The Active Record Pattern + +[Active Record was described by Martin Fowler](http://www.martinfowler.com/eaaCatalog/activeRecord.html) +in his book _Patterns of Enterprise Application Architecture_. In +Active Record, objects carry both persistent data and behavior which +operates on that data. Active Record takes the opinion that ensuring +data access logic as part of the object will educate users of that +object on how to write to and read from the database. + +### Object Relational Mapping + +[Object Relational Mapping](https://en.wikipedia.org/wiki/Object-relational_mapping), commonly referred to as its abbreviation ORM, is +a technique that connects the rich objects of an application to tables in +a relational database management system. Using ORM, the properties and +relationships of the objects in an application can be easily stored and +retrieved from a database without writing SQL statements directly and with less +overall database access code. + +NOTE: If you are not familiar enough with relational database management systems (RDBMS) or structured query language (SQL), please go through [this tutorial](https://www.w3schools.com/sql/default.asp) (or [this one](http://www.sqlcourse.com/)) or study them by other means. Understanding how relational databases work is crucial to understanding Active Records and Rails in general. + +### Active Record as an ORM Framework + +Active Record gives us several mechanisms, the most important being the ability +to: + +* Represent models and their data. +* Represent associations between these models. +* Represent inheritance hierarchies through related models. +* Validate models before they get persisted to the database. +* Perform database operations in an object-oriented fashion. + +Convention over Configuration in Active Record +---------------------------------------------- + +When writing applications using other programming languages or frameworks, it +may be necessary to write a lot of configuration code. This is particularly true +for ORM frameworks in general. However, if you follow the conventions adopted by +Rails, you'll need to write very little configuration (in some cases no +configuration at all) when creating Active Record models. The idea is that if +you configure your applications in the very same way most of the time then this +should be the default way. Thus, explicit configuration would be needed +only in those cases where you can't follow the standard convention. + +### Naming Conventions + +By default, Active Record uses some naming conventions to find out how the +mapping between models and database tables should be created. Rails will +pluralize your class names to find the respective database table. So, for +a class `Book`, you should have a database table called **books**. The Rails +pluralization mechanisms are very powerful, being capable of pluralizing (and +singularizing) both regular and irregular words. When using class names composed +of two or more words, the model class name should follow the Ruby conventions, +using the CamelCase form, while the table name must contain the words separated +by underscores. Examples: + +* Model Class - Singular with the first letter of each word capitalized (e.g., +`BookClub`). +* Database Table - Plural with underscores separating words (e.g., `book_clubs`). + +| Model / Class | Table / Schema | +| ---------------- | -------------- | +| `Article` | `articles` | +| `LineItem` | `line_items` | +| `Deer` | `deers` | +| `Mouse` | `mice` | +| `Person` | `people` | + + +### Schema Conventions + +Active Record uses naming conventions for the columns in database tables, +depending on the purpose of these columns. + +* **Foreign keys** - These fields should be named following the pattern + `singularized_table_name_id` (e.g., `item_id`, `order_id`). These are the + fields that Active Record will look for when you create associations between + your models. +* **Primary keys** - By default, Active Record will use an integer column named + `id` as the table's primary key. When using [Active Record + Migrations](active_record_migrations.html) to create your tables, this column will be + automatically created. + +There are also some optional column names that will add additional features +to Active Record instances: + +* `created_at` - Automatically gets set to the current date and time when the + record is first created. +* `updated_at` - Automatically gets set to the current date and time whenever + the record is created or updated. +* `lock_version` - Adds [optimistic + locking](http://api.rubyonrails.org/classes/ActiveRecord/Locking.html) to + a model. +* `type` - Specifies that the model uses [Single Table + Inheritance](http://api.rubyonrails.org/classes/ActiveRecord/Base.html#class-ActiveRecord::Base-label-Single+table+inheritance). +* `(association_name)_type` - Stores the type for + [polymorphic associations](association_basics.html#polymorphic-associations). +* `(table_name)_count` - Used to cache the number of belonging objects on + associations. For example, a `comments_count` column in an `Article` class that + has many instances of `Comment` will cache the number of existent comments + for each article. + +NOTE: While these column names are optional, they are in fact reserved by Active Record. Steer clear of reserved keywords unless you want the extra functionality. For example, `type` is a reserved keyword used to designate a table using Single Table Inheritance (STI). If you are not using STI, try an analogous keyword like "context", that may still accurately describe the data you are modeling. + +Creating Active Record Models +----------------------------- + +It is very easy to create Active Record models. All you have to do is to +subclass the `ApplicationRecord` class and you're good to go: + +```ruby +class Product < ApplicationRecord +end +``` + +This will create a `Product` model, mapped to a `products` table at the +database. By doing this you'll also have the ability to map the columns of each +row in that table with the attributes of the instances of your model. Suppose +that the `products` table was created using an SQL (or one of its extensions) statement like: + +```sql +CREATE TABLE products ( + id int(11) NOT NULL auto_increment, + name varchar(255), + PRIMARY KEY (id) +); +``` + +Schema above declares a table with two columns: `id` and `name`. Each row of +this table represents a certain product with these two parameters. Thus, you +would be able to write code like the following: + +```ruby +p = Product.new +p.name = "Some Book" +puts p.name # "Some Book" +``` + +Overriding the Naming Conventions +--------------------------------- + +What if you need to follow a different naming convention or need to use your +Rails application with a legacy database? No problem, you can easily override +the default conventions. + +`ApplicationRecord` inherits from `ActiveRecord::Base`, which defines a +number of helpful methods. You can use the `ActiveRecord::Base.table_name=` +method to specify the table name that should be used: + +```ruby +class Product < ApplicationRecord + self.table_name = "my_products" +end +``` + +If you do so, you will have to define manually the class name that is hosting +the fixtures (my_products.yml) using the `set_fixture_class` method in your test +definition: + +```ruby +class ProductTest < ActiveSupport::TestCase + set_fixture_class my_products: Product + fixtures :my_products + ... +end +``` + +It's also possible to override the column that should be used as the table's +primary key using the `ActiveRecord::Base.primary_key=` method: + +```ruby +class Product < ApplicationRecord + self.primary_key = "product_id" +end +``` + +NOTE: Active Record does not support using non-primary key columns named `id`. + +CRUD: Reading and Writing Data +------------------------------ + +CRUD is an acronym for the four verbs we use to operate on data: **C**reate, +**R**ead, **U**pdate and **D**elete. Active Record automatically creates methods +to allow an application to read and manipulate data stored within its tables. + +### Create + +Active Record objects can be created from a hash, a block, or have their +attributes manually set after creation. The `new` method will return a new +object while `create` will return the object and save it to the database. + +For example, given a model `User` with attributes of `name` and `occupation`, +the `create` method call will create and save a new record into the database: + +```ruby +user = User.create(name: "David", occupation: "Code Artist") +``` + +Using the `new` method, an object can be instantiated without being saved: + +```ruby +user = User.new +user.name = "David" +user.occupation = "Code Artist" +``` + +A call to `user.save` will commit the record to the database. + +Finally, if a block is provided, both `create` and `new` will yield the new +object to that block for initialization: + +```ruby +user = User.new do |u| + u.name = "David" + u.occupation = "Code Artist" +end +``` + +### Read + +Active Record provides a rich API for accessing data within a database. Below +are a few examples of different data access methods provided by Active Record. + +```ruby +# return a collection with all users +users = User.all +``` + +```ruby +# return the first user +user = User.first +``` + +```ruby +# return the first user named David +david = User.find_by(name: 'David') +``` + +```ruby +# find all users named David who are Code Artists and sort by created_at in reverse chronological order +users = User.where(name: 'David', occupation: 'Code Artist').order(created_at: :desc) +``` + +You can learn more about querying an Active Record model in the [Active Record +Query Interface](active_record_querying.html) guide. + +### Update + +Once an Active Record object has been retrieved, its attributes can be modified +and it can be saved to the database. + +```ruby +user = User.find_by(name: 'David') +user.name = 'Dave' +user.save +``` + +A shorthand for this is to use a hash mapping attribute names to the desired +value, like so: + +```ruby +user = User.find_by(name: 'David') +user.update(name: 'Dave') +``` + +This is most useful when updating several attributes at once. If, on the other +hand, you'd like to update several records in bulk, you may find the +`update_all` class method useful: + +```ruby +User.update_all "max_login_attempts = 3, must_change_password = 'true'" +``` + +### Delete + +Likewise, once retrieved an Active Record object can be destroyed which removes +it from the database. + +```ruby +user = User.find_by(name: 'David') +user.destroy +``` + +If you'd like to delete several records in bulk, you may use `destroy_all` +method: + +```ruby +# find and delete all users named David +User.where(name: 'David').destroy_all + +# delete all users +User.destroy_all +``` + +Validations +----------- + +Active Record allows you to validate the state of a model before it gets written +into the database. There are several methods that you can use to check your +models and validate that an attribute value is not empty, is unique and not +already in the database, follows a specific format, and many more. + +Validation is a very important issue to consider when persisting to the database, so +the methods `save` and `update` take it into account when +running: they return `false` when validation fails and they don't actually +perform any operations on the database. All of these have a bang counterpart (that +is, `save!` and `update!`), which are stricter in that +they raise the exception `ActiveRecord::RecordInvalid` if validation fails. +A quick example to illustrate: + +```ruby +class User < ApplicationRecord + validates :name, presence: true +end + +user = User.new +user.save # => false +user.save! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank +``` + +You can learn more about validations in the [Active Record Validations +guide](active_record_validations.html). + +Callbacks +--------- + +Active Record callbacks allow you to attach code to certain events in the +life-cycle of your models. This enables you to add behavior to your models by +transparently executing code when those events occur, like when you create a new +record, update it, destroy it, and so on. You can learn more about callbacks in +the [Active Record Callbacks guide](active_record_callbacks.html). + +Migrations +---------- + +Rails provides a domain-specific language for managing a database schema called +migrations. Migrations are stored in files which are executed against any +database that Active Record supports using `rake`. Here's a migration that +creates a table: + +```ruby +class CreatePublications < ActiveRecord::Migration[5.0] + def change + create_table :publications do |t| + t.string :title + t.text :description + t.references :publication_type + t.integer :publisher_id + t.string :publisher_type + t.boolean :single_issue + + t.timestamps + end + add_index :publications, :publication_type_id + end +end +``` + +Rails keeps track of which files have been committed to the database and +provides rollback features. To actually create the table, you'd run `rails db:migrate` +and to roll it back, `rails db:rollback`. + +Note that the above code is database-agnostic: it will run in MySQL, +PostgreSQL, Oracle, and others. You can learn more about migrations in the +[Active Record Migrations guide](active_record_migrations.html). |