diff options
Diffstat (limited to 'guides/source/association_basics.md')
-rw-r--r-- | guides/source/association_basics.md | 459 |
1 files changed, 337 insertions, 122 deletions
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index 42c7c07745..c0482f6106 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -1,11 +1,13 @@ -A Guide to Active Record Associations -===================================== +Active Record Associations +========================== -This guide covers the association features of Active Record. By referring to this guide, you will be able to: +This guide covers the association features of Active Record. -* Declare associations between Active Record models -* Understand the various types of Active Record associations -* Use the methods added to your models by creating associations +After reading this guide, you will know: + +* How to declare associations between Active Record models. +* How to understand the various types of Active Record associations. +* How to use the methods added to your models by creating associations. -------------------------------------------------------------------------------- @@ -25,25 +27,24 @@ end Now, suppose we wanted to add a new order for an existing customer. We'd need to do something like this: ```ruby -@order = Order.create(:order_date => Time.now, - :customer_id => @customer.id) +@order = Order.create(order_date: Time.now, customer_id: @customer.id) ``` Or consider deleting a customer, and ensuring that all of its orders get deleted as well: ```ruby -@orders = Order.where(:customer_id => @customer.id) +@orders = Order.where(customer_id: @customer.id) @orders.each do |order| order.destroy end @customer.destroy ``` -With Active Record associations, we can streamline these -- and other -- operations by declaratively telling Rails that there is a connection between the two models. Here's the revised code for setting up customers and orders: +With Active Record associations, we can streamline these - and other - operations by declaratively telling Rails that there is a connection between the two models. Here's the revised code for setting up customers and orders: ```ruby class Customer < ActiveRecord::Base - has_many :orders, :dependent => :destroy + has_many :orders, dependent: :destroy end class Order < ActiveRecord::Base @@ -54,10 +55,10 @@ end With this change, creating a new order for a particular customer is easier: ```ruby -@order = @customer.orders.create(:order_date => Time.now) +@order = @customer.orders.create(order_date: Time.now) ``` -Deleting a customer and all of its orders is _much_ easier: +Deleting a customer and all of its orders is *much* easier: ```ruby @customer.destroy @@ -68,7 +69,7 @@ To learn more about the different types of associations, read the next section o The Types of Associations ------------------------- -In Rails, an _association_ is a connection between two Active Record models. Associations are implemented using macro-style calls, so that you can declaratively add features to your models. For example, by declaring that one model `belongs_to` another, you instruct Rails to maintain Primary Key–Foreign Key information between instances of the two models, and you also get a number of utility methods added to your model. Rails supports six types of associations: +In Rails, an _association_ is a connection between two Active Record models. Associations are implemented using macro-style calls, so that you can declaratively add features to your models. For example, by declaring that one model `belongs_to` another, you instruct Rails to maintain Primary Key-Foreign Key information between instances of the two models, and you also get a number of utility methods added to your model. Rails supports six types of associations: * `belongs_to` * `has_one` @@ -93,6 +94,25 @@ end NOTE: `belongs_to` associations _must_ use the singular term. If you used the pluralized form in the above example for the `customer` association in the `Order` model, you would be told that there was an "uninitialized constant Order::Customers". This is because Rails automatically infers the class name from the association name. If the association name is wrongly pluralized, then the inferred class will be wrongly pluralized too. +The corresponding migration might look like this: + +```ruby +class CreateOrders < ActiveRecord::Migration + def change + create_table :customers do |t| + t.string :name + t.timestamps + end + + create_table :orders do |t| + t.belongs_to :customer + t.datetime :order_date + t.timestamps + end + end +end +``` + ### The `has_one` Association A `has_one` association also sets up a one-to-one connection with another model, but with somewhat different semantics (and consequences). This association indicates that each instance of a model contains or possesses one instance of another model. For example, if each supplier in your application has only one account, you'd declare the supplier model like this: @@ -105,6 +125,25 @@ end  +The corresponding migration might look like this: + +```ruby +class CreateSuppliers < ActiveRecord::Migration + def change + create_table :suppliers do |t| + t.string :name + t.timestamps + end + + create_table :accounts do |t| + t.belongs_to :supplier + t.string :account_number + t.timestamps + end + end +end +``` + ### The `has_many` Association A `has_many` association indicates a one-to-many connection with another model. You'll often find this association on the "other side" of a `belongs_to` association. This association indicates that each instance of the model has zero or more instances of another model. For example, in an application containing customers and orders, the customer model could be declared like this: @@ -119,6 +158,25 @@ NOTE: The name of the other model is pluralized when declaring a `has_many` asso  +The corresponding migration might look like this: + +```ruby +class CreateCustomers < ActiveRecord::Migration + def change + create_table :customers do |t| + t.string :name + t.timestamps + end + + create_table :orders do |t| + t.belongs_to :customer + t.datetime :order_date + t.timestamps + end + end +end +``` + ### The `has_many :through` Association A `has_many :through` association is often used to set up a many-to-many connection with another model. This association indicates that the declaring model can be matched with zero or more instances of another model by proceeding _through_ a third model. For example, consider a medical practice where patients make appointments to see physicians. The relevant association declarations could look like this: @@ -126,7 +184,7 @@ A `has_many :through` association is often used to set up a many-to-many connect ```ruby class Physician < ActiveRecord::Base has_many :appointments - has_many :patients, :through => :appointments + has_many :patients, through: :appointments end class Appointment < ActiveRecord::Base @@ -136,12 +194,37 @@ end class Patient < ActiveRecord::Base has_many :appointments - has_many :physicians, :through => :appointments + has_many :physicians, through: :appointments end ```  +The corresponding migration might look like this: + +```ruby +class CreateAppointments < ActiveRecord::Migration + def change + create_table :physicians do |t| + t.string :name + t.timestamps + end + + create_table :patients do |t| + t.string :name + t.timestamps + end + + create_table :appointments do |t| + t.belongs_to :physician + t.belongs_to :patient + t.datetime :appointment_date + t.timestamps + end + end +end +``` + The collection of join models can be managed via the API. For example, if you assign ```ruby @@ -157,7 +240,7 @@ The `has_many :through` association is also useful for setting up "shortcuts" th ```ruby class Document < ActiveRecord::Base has_many :sections - has_many :paragraphs, :through => :sections + has_many :paragraphs, through: :sections end class Section < ActiveRecord::Base @@ -170,7 +253,7 @@ class Paragraph < ActiveRecord::Base end ``` -With `:through => :sections` specified, Rails will now understand: +With `through: :sections` specified, Rails will now understand: ```ruby @document.paragraphs @@ -178,12 +261,15 @@ With `:through => :sections` specified, Rails will now understand: ### The `has_one :through` Association -A `has_one :through` association sets up a one-to-one connection with another model. This association indicates that the declaring model can be matched with one instance of another model by proceeding _through_ a third model. For example, if each supplier has one account, and each account is associated with one account history, then the customer model could look like this: +A `has_one :through` association sets up a one-to-one connection with another model. This association indicates +that the declaring model can be matched with one instance of another model by proceeding _through_ a third model. +For example, if each supplier has one account, and each account is associated with one account history, then the +supplier model could look like this: ```ruby class Supplier < ActiveRecord::Base has_one :account - has_one :account_history, :through => :account + has_one :account_history, through: :account end class Account < ActiveRecord::Base @@ -198,6 +284,31 @@ end  +The corresponding migration might look like this: + +```ruby +class CreateAccountHistories < ActiveRecord::Migration + def change + create_table :suppliers do |t| + t.string :name + t.timestamps + end + + create_table :accounts do |t| + t.belongs_to :supplier + t.string :account_number + t.timestamps + end + + create_table :account_histories do |t| + t.belongs_to :account + t.integer :credit_rating + t.timestamps + end + end +end +``` + ### The `has_and_belongs_to_many` Association A `has_and_belongs_to_many` association creates a direct many-to-many connection with another model, with no intervening model. For example, if your application includes assemblies and parts, with each assembly having many parts and each part appearing in many assemblies, you could declare the models this way: @@ -214,6 +325,29 @@ end  +The corresponding migration might look like this: + +```ruby +class CreateAssembliesAndParts < ActiveRecord::Migration + def change + create_table :assemblies do |t| + t.string :name + t.timestamps + end + + create_table :parts do |t| + t.string :part_number + t.timestamps + end + + create_table :assemblies_parts, id: false do |t| + t.belongs_to :assembly + t.belongs_to :part + end + end +end +``` + ### Choosing Between `belongs_to` and `has_one` If you want to set up a one-to-one relationship between two models, you'll need to add `belongs_to` to one, and `has_one` to the other. How do you know which is which? @@ -270,7 +404,7 @@ The second way to declare a many-to-many relationship is to use `has_many :throu ```ruby class Assembly < ActiveRecord::Base has_many :manifests - has_many :parts, :through => :manifests + has_many :parts, through: :manifests end class Manifest < ActiveRecord::Base @@ -280,7 +414,7 @@ end class Part < ActiveRecord::Base has_many :manifests - has_many :assemblies, :through => :manifests + has_many :assemblies, through: :manifests end ``` @@ -294,15 +428,15 @@ A slightly more advanced twist on associations is the _polymorphic association_. ```ruby class Picture < ActiveRecord::Base - belongs_to :imageable, :polymorphic => true + belongs_to :imageable, polymorphic: true end class Employee < ActiveRecord::Base - has_many :pictures, :as => :imageable + has_many :pictures, as: :imageable end class Product < ActiveRecord::Base - has_many :pictures, :as => :imageable + has_many :pictures, as: :imageable end ``` @@ -332,7 +466,7 @@ class CreatePictures < ActiveRecord::Migration def change create_table :pictures do |t| t.string :name - t.references :imageable, :polymorphic => true + t.references :imageable, polymorphic: true t.timestamps end end @@ -347,9 +481,10 @@ In designing a data model, you will sometimes find a model that should have a re ```ruby class Employee < ActiveRecord::Base - has_many :subordinates, :class_name => "Employee", - :foreign_key => "manager_id" - belongs_to :manager, :class_name => "Employee" + has_many :subordinates, class_name: "Employee", + foreign_key: "manager_id" + + belongs_to :manager, class_name: "Employee" end ``` @@ -440,9 +575,9 @@ end These need to be backed up by a migration to create the `assemblies_parts` table. This table should be created without a primary key: ```ruby -class CreateAssemblyPartJoinTable < ActiveRecord::Migration +class CreateAssembliesPartsJoinTable < ActiveRecord::Migration def change - create_table :assemblies_parts, :id => false do |t| + create_table :assemblies_parts, id: false do |t| t.integer :assembly_id t.integer :part_id end @@ -450,7 +585,7 @@ class CreateAssemblyPartJoinTable < ActiveRecord::Migration end ``` -We pass `:id => false` to `create_table` because that table does not represent a model. That's required for the association to work properly. If you observe any strange behavior in a `has_and_belongs_to_many` association like mangled models IDs, or exceptions about conflicting IDs chances are you forgot that bit. +We pass `id: false` to `create_table` because that table does not represent a model. That's required for the association to work properly. If you observe any strange behavior in a `has_and_belongs_to_many` association like mangled models IDs, or exceptions about conflicting IDs, chances are you forgot that bit. ### Controlling Association Scope @@ -495,14 +630,14 @@ module MyApplication module Business class Supplier < ActiveRecord::Base has_one :account, - :class_name => "MyApplication::Billing::Account" + class_name: "MyApplication::Billing::Account" end end module Billing class Account < ActiveRecord::Base belongs_to :supplier, - :class_name => "MyApplication::Business::Supplier" + class_name: "MyApplication::Business::Supplier" end end end @@ -536,11 +671,11 @@ This happens because c and o.customer are two different in-memory representation ```ruby class Customer < ActiveRecord::Base - has_many :orders, :inverse_of => :customer + has_many :orders, inverse_of: :customer end class Order < ActiveRecord::Base - belongs_to :customer, :inverse_of => :orders + belongs_to :customer, inverse_of: :orders end ``` @@ -561,6 +696,17 @@ There are a few limitations to `inverse_of` support: * They do not work with `:as` associations. * For `belongs_to` associations, `has_many` inverse associations are ignored. +Every association will attempt to automatically find the inverse association +and set the `:inverse_of` option heuristically (based on the association name). +Most associations with standard names will be supported. However, associations +that contain the following options will not have their inverses set +automatically: + +* :conditions +* :through +* :polymorphic +* :foreign_key + Detailed Association Reference ------------------------------ @@ -572,12 +718,13 @@ The `belongs_to` association creates a one-to-one match with another model. In d #### Methods Added by `belongs_to` -When you declare a `belongs_to` association, the declaring class automatically gains four methods related to the association: +When you declare a `belongs_to` association, the declaring class automatically gains five methods related to the association: * `association(force_reload = false)` * `association=(associate)` * `build_association(attributes = {})` * `create_association(attributes = {})` +* `create_association!(attributes = {})` In all of these methods, `association` is replaced with the symbol passed as the first argument to `belongs_to`. For example, given the declaration: @@ -594,6 +741,7 @@ customer customer= build_customer create_customer +create_customer! ``` NOTE: When initializing a new `has_one` or `belongs_to` association you must use the `build_` prefix to build the association, rather than the `association.build` method that would be used for `has_many` or `has_and_belongs_to_many` associations. To create one, use the `create_` prefix. @@ -621,8 +769,8 @@ The `association=` method assigns an associated object to this object. Behind th The `build_association` method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through this object's foreign key will be set, but the associated object will _not_ yet be saved. ```ruby -@customer = @order.build_customer(:customer_number => 123, - :customer_name => "John Doe") +@customer = @order.build_customer(customer_number: 123, + customer_name: "John Doe") ``` ##### `create_association(attributes = {})` @@ -630,19 +778,23 @@ The `build_association` method returns a new object of the associated type. This The `create_association` method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through this object's foreign key will be set, and, once it passes all of the validations specified on the associated model, the associated object _will_ be saved. ```ruby -@customer = @order.create_customer(:customer_number => 123, - :customer_name => "John Doe") +@customer = @order.create_customer(customer_number: 123, + customer_name: "John Doe") ``` +##### `create_association!(attributes = {})` + +Does the same as `create_association` above, but raises `ActiveRecord::RecordInvalid` if the record is invalid. + #### Options for `belongs_to` -While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `belongs_to` association reference. Such customizations can easily be accomplished by passing options and scope blocks when you create the association. For example, this assocation uses two such options: +While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `belongs_to` association reference. Such customizations can easily be accomplished by passing options and scope blocks when you create the association. For example, this association uses two such options: ```ruby class Order < ActiveRecord::Base - belongs_to :customer, :dependent => :destroy, - :counter_cache => true + belongs_to :customer, dependent: :destroy, + counter_cache: true end ``` @@ -668,7 +820,7 @@ If the name of the other model cannot be derived from the association name, you ```ruby class Order < ActiveRecord::Base - belongs_to :customer, :class_name => "Patron" + belongs_to :customer, class_name: "Patron" end ``` @@ -689,7 +841,7 @@ With these declarations, asking for the value of `@customer.orders.size` require ```ruby class Order < ActiveRecord::Base - belongs_to :customer, :counter_cache => true + belongs_to :customer, counter_cache: true end class Customer < ActiveRecord::Base has_many :orders @@ -702,7 +854,7 @@ Although the `:counter_cache` option is specified on the model that includes the ```ruby class Order < ActiveRecord::Base - belongs_to :customer, :counter_cache => :count_of_orders + belongs_to :customer, counter_cache: :count_of_orders end class Customer < ActiveRecord::Base has_many :orders @@ -713,7 +865,7 @@ Counter cache columns are added to the containing model's list of read-only attr ##### `:dependent` -If you set the `:dependent` option to `:destroy`, then deleting this object will call the `destroy` method on the associated object to delete that object. If you set the `:dependent` option to `:delete`, then deleting this object will delete the associated object _without_ calling its `destroy` method. +If you set the `:dependent` option to `:destroy`, then deleting this object will call the `destroy` method on the associated object to delete that object. If you set the `:dependent` option to `:delete`, then deleting this object will delete the associated object _without_ calling its `destroy` method. If you set the `:dependent` option to `:restrict`, then attempting to delete this object will result in a `ActiveRecord::DeleteRestrictionError` if there are any associated objects. WARNING: You should not specify this option on a `belongs_to` association that is connected with a `has_many` association on the other class. Doing so can lead to orphaned records in your database. @@ -723,8 +875,8 @@ By convention, Rails assumes that the column used to hold the foreign key on thi ```ruby class Order < ActiveRecord::Base - belongs_to :customer, :class_name => "Patron", - :foreign_key => "patron_id" + belongs_to :customer, class_name: "Patron", + foreign_key: "patron_id" end ``` @@ -736,11 +888,11 @@ The `:inverse_of` option specifies the name of the `has_many` or `has_one` assoc ```ruby class Customer < ActiveRecord::Base - has_many :orders, :inverse_of => :customer + has_many :orders, inverse_of: :customer end class Order < ActiveRecord::Base - belongs_to :customer, :inverse_of => :orders + belongs_to :customer, inverse_of: :orders end ``` @@ -754,7 +906,7 @@ If you set the `:touch` option to `:true`, then the `updated_at` or `updated_on` ```ruby class Order < ActiveRecord::Base - belongs_to :customer, :touch => true + belongs_to :customer, touch: true end class Customer < ActiveRecord::Base @@ -766,7 +918,7 @@ In this case, saving or destroying an order will update the timestamp on the ass ```ruby class Order < ActiveRecord::Base - belongs_to :customer, :touch => :orders_updated_at + belongs_to :customer, touch: :orders_updated_at end ``` @@ -780,8 +932,8 @@ There may be times when you wish to customize the query used by `belongs_to`. Su ```ruby class Order < ActiveRecord::Base - belongs_to :customer, -> { where :active => true }, - :dependent => :destroy + belongs_to :customer, -> { where active: true }, + dependent: :destroy end ``` @@ -798,13 +950,13 @@ The `where` method lets you specify the conditions that the associated object mu ```ruby class Order < ActiveRecord::Base - belongs_to :customer, -> { where :active => true } + belongs_to :customer, -> { where active: true } end ``` ##### `includes` -You can use the `includes` method let you specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: +You can use the `includes` method to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: ```ruby class LineItem < ActiveRecord::Base @@ -870,12 +1022,13 @@ The `has_one` association creates a one-to-one match with another model. In data #### Methods Added by `has_one` -When you declare a `has_one` association, the declaring class automatically gains four methods related to the association: +When you declare a `has_one` association, the declaring class automatically gains five methods related to the association: * `association(force_reload = false)` * `association=(associate)` * `build_association(attributes = {})` * `create_association(attributes = {})` +* `create_association!(attributes = {})` In all of these methods, `association` is replaced with the symbol passed as the first argument to `has_one`. For example, given the declaration: @@ -892,6 +1045,7 @@ account account= build_account create_account +create_account! ``` NOTE: When initializing a new `has_one` or `belongs_to` association you must use the `build_` prefix to build the association, rather than the `association.build` method that would be used for `has_many` or `has_and_belongs_to_many` associations. To create one, use the `create_` prefix. @@ -919,7 +1073,7 @@ The `association=` method assigns an associated object to this object. Behind th The `build_association` method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through its foreign key will be set, but the associated object will _not_ yet be saved. ```ruby -@account = @supplier.build_account(:terms => "Net 30") +@account = @supplier.build_account(terms: "Net 30") ``` ##### `create_association(attributes = {})` @@ -927,16 +1081,20 @@ The `build_association` method returns a new object of the associated type. This The `create_association` method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through its foreign key will be set, and, once it passes all of the validations specified on the associated model, the associated object _will_ be saved. ```ruby -@account = @supplier.create_account(:terms => "Net 30") +@account = @supplier.create_account(terms: "Net 30") ``` +##### `create_association!(attributes = {})` + +Does the same as `create_association` above, but raises `ActiveRecord::RecordInvalid` if the record is invalid. + #### Options for `has_one` -While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_one` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this assocation uses two such options: +While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_one` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options: ```ruby class Supplier < ActiveRecord::Base - has_one :account, :class_name => "Billing", :dependent => :nullify + has_one :account, class_name: "Billing", dependent: :nullify end ``` @@ -968,7 +1126,7 @@ If the name of the other model cannot be derived from the association name, you ```ruby class Supplier < ActiveRecord::Base - has_one :account, :class_name => "Billing" + has_one :account, class_name: "Billing" end ``` @@ -977,18 +1135,24 @@ end Controls what happens to the associated object when its owner is destroyed: * `:destroy` causes the associated object to also be destroyed -* `:delete` causes the asssociated object to be deleted directly from the database (so callbacks will not execute) +* `:delete` causes the associated object to be deleted directly from the database (so callbacks will not execute) * `:nullify` causes the foreign key to be set to `NULL`. Callbacks are not executed. * `:restrict_with_exception` causes an exception to be raised if there is an associated record * `:restrict_with_error` causes an error to be added to the owner if there is an associated object +It's necessary not to set or leave `:nullify` option for those associations +that have `NOT NULL` database constraints. If you don't set `dependent` to +destroy such associations you won't be able to change the associated object +because initial associated object foreign key will be set to unallowed `NULL` +value. + ##### `:foreign_key` By convention, Rails assumes that the column used to hold the foreign key on the other model is the name of this model with the suffix `_id` added. The `:foreign_key` option lets you set the name of the foreign key directly: ```ruby class Supplier < ActiveRecord::Base - has_one :account, :foreign_key => "supp_id" + has_one :account, foreign_key: "supp_id" end ``` @@ -1000,11 +1164,11 @@ The `:inverse_of` option specifies the name of the `belongs_to` association that ```ruby class Supplier < ActiveRecord::Base - has_one :account, :inverse_of => :supplier + has_one :account, inverse_of: :supplier end class Account < ActiveRecord::Base - belongs_to :supplier, :inverse_of => :account + belongs_to :supplier, inverse_of: :account end ``` @@ -1034,7 +1198,7 @@ There may be times when you wish to customize the query used by `has_one`. Such ```ruby class Supplier < ActiveRecord::Base - has_one :account, -> { where :active => true } + has_one :account, -> { where active: true } end ``` @@ -1125,7 +1289,7 @@ The `has_many` association creates a one-to-many relationship with another model #### Methods Added by `has_many` -When you declare a `has_many` association, the declaring class automatically gains 13 methods related to the association: +When you declare a `has_many` association, the declaring class automatically gains 16 methods related to the association: * `collection(force_reload = false)` * `collection<<(object, ...)` @@ -1142,8 +1306,9 @@ When you declare a `has_many` association, the declaring class automatically gai * `collection.exists?(...)` * `collection.build(attributes = {}, ...)` * `collection.create(attributes = {})` +* `collection.create!(attributes = {})` -In all of these methods, `collection` is replaced with the symbol passed as the first argument to `has_many`, and `collection_singular` is replaced with the singularized version of that symbol.. For example, given the declaration: +In all of these methods, `collection` is replaced with the symbol passed as the first argument to `has_many`, and `collection_singular` is replaced with the singularized version of that symbol. For example, given the declaration: ```ruby class Customer < ActiveRecord::Base @@ -1169,6 +1334,7 @@ orders.where(...) orders.exists?(...) orders.build(attributes = {}, ...) orders.create(attributes = {}) +orders.create!(attributes = {}) ``` ##### `collection(force_reload = false)` @@ -1195,7 +1361,7 @@ The `collection.delete` method removes one or more objects from the collection b @customer.orders.delete(@order1) ``` -WARNING: Additionally, objects will be destroyed if they're associated with `:dependent => :destroy`, and deleted if they're associated with `:dependent => :delete_all`. +WARNING: Additionally, objects will be destroyed if they're associated with `dependent: :destroy`, and deleted if they're associated with `dependent: :delete_all`. ##### `collection.destroy(object, ...)` @@ -1225,7 +1391,7 @@ The `collection_singular_ids=` method makes the collection contain only the obje ##### `collection.clear` -The `collection.clear` method removes every object from the collection. This destroys the associated objects if they are associated with `:dependent => :destroy`, deletes them directly from the database if `:dependent => :delete_all`, and otherwise sets their foreign keys to `NULL`. +The `collection.clear` method removes every object from the collection. This destroys the associated objects if they are associated with `dependent: :destroy`, deletes them directly from the database if `dependent: :delete_all`, and otherwise sets their foreign keys to `NULL`. ##### `collection.empty?` @@ -1258,7 +1424,7 @@ The `collection.find` method finds objects within the collection. It uses the sa The `collection.where` method finds objects within the collection based on the conditions supplied but the objects are loaded lazily meaning that the database is queried only when the object(s) are accessed. ```ruby -@open_orders = @customer.orders.where(:open => true) # No query yet +@open_orders = @customer.orders.where(open: true) # No query yet @open_order = @open_orders.first # Now the database will be queried ``` @@ -1271,8 +1437,8 @@ The `collection.exists?` method checks whether an object meeting the supplied co The `collection.build` method returns one or more new objects of the associated type. These objects will be instantiated from the passed attributes, and the link through their foreign key will be created, but the associated objects will _not_ yet be saved. ```ruby -@order = @customer.orders.build(:order_date => Time.now, - :order_number => "A12345") +@order = @customer.orders.build(order_date: Time.now, + order_number: "A12345") ``` ##### `collection.create(attributes = {})` @@ -1280,17 +1446,21 @@ The `collection.build` method returns one or more new objects of the associated The `collection.create` method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through its foreign key will be created, and, once it passes all of the validations specified on the associated model, the associated object _will_ be saved. ```ruby -@order = @customer.orders.create(:order_date => Time.now, - :order_number => "A12345") +@order = @customer.orders.create(order_date: Time.now, + order_number: "A12345") ``` +##### `collection.create!(attributes = {})` + +Does the same as `collection.create` above, but raises `ActiveRecord::RecordInvalid` if the record is invalid. + #### Options for `has_many` -While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_many` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this assocation uses two such options: +While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_many` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options: ```ruby class Customer < ActiveRecord::Base - has_many :orders, :dependent => :delete_all, :validate => :false + has_many :orders, dependent: :delete_all, validate: :false end ``` @@ -1322,7 +1492,7 @@ If the name of the other model cannot be derived from the association name, you ```ruby class Customer < ActiveRecord::Base - has_many :orders, :class_name => "Transaction" + has_many :orders, class_name: "Transaction" end ``` @@ -1331,7 +1501,7 @@ end Controls what happens to the associated objects when their owner is destroyed: * `:destroy` causes all the associated objects to also be destroyed -* `:delete_all` causes all the asssociated objects to be deleted directly from the database (so callbacks will not execute) +* `:delete_all` causes all the associated objects to be deleted directly from the database (so callbacks will not execute) * `:nullify` causes the foreign keys to be set to `NULL`. Callbacks are not executed. * `:restrict_with_exception` causes an exception to be raised if there are any associated records * `:restrict_with_error` causes an error to be added to the owner if there are any associated objects @@ -1344,7 +1514,7 @@ By convention, Rails assumes that the column used to hold the foreign key on the ```ruby class Customer < ActiveRecord::Base - has_many :orders, :foreign_key => "cust_id" + has_many :orders, foreign_key: "cust_id" end ``` @@ -1356,11 +1526,11 @@ The `:inverse_of` option specifies the name of the `belongs_to` association that ```ruby class Customer < ActiveRecord::Base - has_many :orders, :inverse_of => :customer + has_many :orders, inverse_of: :customer end class Order < ActiveRecord::Base - belongs_to :customer, :inverse_of => :orders + belongs_to :customer, inverse_of: :orders end ``` @@ -1368,6 +1538,20 @@ end By convention, Rails assumes that the column used to hold the primary key of the association is `id`. You can override this and explicitly specify the primary key with the `:primary_key` option. +Let's say that `users` table has `id` as the primary_key but it also has +`guid` column. And the requirement is that `todos` table should hold +`guid` column value and not `id` value. This can be achieved like this + +```ruby +class User < ActiveRecord::Base + has_many :todos, primary_key: :guid +end +``` + +Now if we execute `@user.todos.create` then `@todo` record will have +`user_id` value as the `guid` value of `@user`. + + ##### `:source` The `:source` option specifies the source association name for a `has_many :through` association. You only need to use this option if the name of the source association cannot be automatically inferred from the association name. @@ -1390,7 +1574,7 @@ There may be times when you wish to customize the query used by `has_many`. Such ```ruby class Customer < ActiveRecord::Base - has_many :orders, -> { where :processed => true } + has_many :orders, -> { where processed: true } end ``` @@ -1414,7 +1598,7 @@ The `where` method lets you specify the conditions that the associated object mu ```ruby class Customer < ActiveRecord::Base has_many :confirmed_orders, -> { where "confirmed = 1" }, - :class_name => "Order" + class_name: "Order" end ``` @@ -1422,8 +1606,8 @@ You can also set conditions via a hash: ```ruby class Customer < ActiveRecord::Base - has_many :confirmed_orders, -> { where :confirmed => true }, - :class_name => "Order" + has_many :confirmed_orders, -> { where confirmed: true }, + class_name: "Order" end ``` @@ -1440,7 +1624,7 @@ The `group` method supplies an attribute name to group the result set by, using ```ruby class Customer < ActiveRecord::Base has_many :line_items, -> { group 'orders.id' }, - :through => :orders + through: :orders end ``` @@ -1488,7 +1672,7 @@ The `limit` method lets you restrict the total number of objects that will be fe class Customer < ActiveRecord::Base has_many :recent_orders, -> { order('order_date desc').limit(100) }, - :class_name => "Order", + class_name: "Order", end ``` @@ -1516,43 +1700,67 @@ The `select` method lets you override the SQL `SELECT` clause that is used to re WARNING: If you specify your own `select`, be sure to include the primary key and foreign key columns of the associated model. If you do not, Rails will throw an error. -##### `uniq` +##### `distinct` -Use the `uniq` method to keep the collection free of duplicates. This is mostly useful together with the `:through` option. +Use the `distinct` method to keep the collection free of duplicates. This is +mostly useful together with the `:through` option. ```ruby class Person < ActiveRecord::Base has_many :readings - has_many :posts, :through => :readings + has_many :posts, through: :readings end -person = Person.create(:name => 'john') -post = Post.create(:name => 'a1') +person = Person.create(name: 'John') +post = Post.create(name: 'a1') person.posts << post person.posts << post person.posts.inspect # => [#<Post id: 5, name: "a1">, #<Post id: 5, name: "a1">] Reading.all.inspect # => [#<Reading id: 12, person_id: 5, post_id: 5>, #<Reading id: 13, person_id: 5, post_id: 5>] ``` -In the above case there are two readings and `person.posts` brings out both of them even though these records are pointing to the same post. +In the above case there are two readings and `person.posts` brings out both of +them even though these records are pointing to the same post. -Now let's set `uniq`: +Now let's set `distinct`: ```ruby class Person has_many :readings - has_many :posts, -> { uniq }, :through => :readings + has_many :posts, -> { distinct }, through: :readings end -person = Person.create(:name => 'honda') -post = Post.create(:name => 'a1') +person = Person.create(name: 'Honda') +post = Post.create(name: 'a1') person.posts << post person.posts << post person.posts.inspect # => [#<Post id: 7, name: "a1">] Reading.all.inspect # => [#<Reading id: 16, person_id: 7, post_id: 7>, #<Reading id: 17, person_id: 7, post_id: 7>] ``` -In the above case there are still two readings. However `person.posts` shows only one post because the collection loads only unique records. +In the above case there are still two readings. However `person.posts` shows +only one post because the collection loads only unique records. + +If you want to make sure that, upon insertion, all of the records in the +persisted association are distinct (so that you can be sure that when you +inspect the association that you will never find duplicate records), you should +add a unique index on the table itself. For example, if you have a table named +`person_posts` and you want to make sure all the posts are unique, you could +add the following in a migration: + +```ruby +add_index :person_posts, :post, unique: true +``` + +Note that checking for uniqueness using something like `include?` is subject +to race conditions. Do not attempt to use `include?` to enforce distinctness +in an association. For instance, using the post example from above, the +following code would be racy because multiple users could be attempting this +at the same time: + +```ruby +person.posts << post unless person.posts.include?(post) +``` #### When are Objects Saved? @@ -1570,7 +1778,7 @@ The `has_and_belongs_to_many` association creates a many-to-many relationship wi #### Methods Added by `has_and_belongs_to_many` -When you declare a `has_and_belongs_to_many` association, the declaring class automatically gains 13 methods related to the association: +When you declare a `has_and_belongs_to_many` association, the declaring class automatically gains 16 methods related to the association: * `collection(force_reload = false)` * `collection<<(object, ...)` @@ -1587,6 +1795,7 @@ When you declare a `has_and_belongs_to_many` association, the declaring class au * `collection.exists?(...)` * `collection.build(attributes = {})` * `collection.create(attributes = {})` +* `collection.create!(attributes = {})` In all of these methods, `collection` is replaced with the symbol passed as the first argument to `has_and_belongs_to_many`, and `collection_singular` is replaced with the singularized version of that symbol. For example, given the declaration: @@ -1614,6 +1823,7 @@ assemblies.where(...) assemblies.exists?(...) assemblies.build(attributes = {}, ...) assemblies.create(attributes = {}) +assemblies.create!(attributes = {}) ``` ##### Additional Column Methods @@ -1722,8 +1932,7 @@ The `collection.exists?` method checks whether an object meeting the supplied co The `collection.build` method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through the join table will be created, but the associated object will _not_ yet be saved. ```ruby -@assembly = @part.assemblies.build( - {:assembly_name => "Transmission housing"}) +@assembly = @part.assemblies.build({assembly_name: "Transmission housing"}) ``` ##### `collection.create(attributes = {})` @@ -1731,18 +1940,21 @@ The `collection.build` method returns a new object of the associated type. This The `collection.create` method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through the join table will be created, and, once it passes all of the validations specified on the associated model, the associated object _will_ be saved. ```ruby -@assembly = @part.assemblies.create( - {:assembly_name => "Transmission housing"}) +@assembly = @part.assemblies.create({assembly_name: "Transmission housing"}) ``` +##### `collection.create!(attributes = {})` + +Does the same as `collection.create`, but raises `ActiveRecord::RecordInvalid` if the record is invalid. + #### Options for `has_and_belongs_to_many` -While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_and_belongs_to_many` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this assocation uses two such options: +While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_and_belongs_to_many` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options: ```ruby class Parts < ActiveRecord::Base - has_and_belongs_to_many :assemblies, :uniq => true, - :read_only => true + has_and_belongs_to_many :assemblies, autosave: true, + readonly: true end ``` @@ -1754,6 +1966,7 @@ The `has_and_belongs_to_many` association supports these options: * `:foreign_key` * `:join_table` * `:validate` +* `:readonly` ##### `:association_foreign_key` @@ -1763,9 +1976,10 @@ TIP: The `:foreign_key` and `:association_foreign_key` options are useful when s ```ruby class User < ActiveRecord::Base - has_and_belongs_to_many :friends, :class_name => "User", - :foreign_key => "this_user_id", - :association_foreign_key => "other_user_id" + has_and_belongs_to_many :friends, + class_name: "User", + foreign_key: "this_user_id", + association_foreign_key: "other_user_id" end ``` @@ -1779,7 +1993,7 @@ If the name of the other model cannot be derived from the association name, you ```ruby class Parts < ActiveRecord::Base - has_and_belongs_to_many :assemblies, :class_name => "Gadget" + has_and_belongs_to_many :assemblies, class_name: "Gadget" end ``` @@ -1789,9 +2003,10 @@ By convention, Rails assumes that the column in the join table used to hold the ```ruby class User < ActiveRecord::Base - has_and_belongs_to_many :friends, :class_name => "User", - :foreign_key => "this_user_id", - :association_foreign_key => "other_user_id" + has_and_belongs_to_many :friends, + class_name: "User", + foreign_key: "this_user_id", + association_foreign_key: "other_user_id" end ``` @@ -1809,7 +2024,7 @@ There may be times when you wish to customize the query used by `has_and_belongs ```ruby class Parts < ActiveRecord::Base - has_and_belongs_to_many :assemblies, -> { where :active => true } + has_and_belongs_to_many :assemblies, -> { where active: true } end ``` @@ -1842,7 +2057,7 @@ You can also set conditions via a hash: ```ruby class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, - -> { where :factory => 'Seattle' } + -> { where factory: 'Seattle' } end ``` @@ -1929,7 +2144,7 @@ You define association callbacks by adding options to the association declaratio ```ruby class Customer < ActiveRecord::Base - has_many :orders, :before_add => :check_credit_limit + has_many :orders, before_add: :check_credit_limit def check_credit_limit(order) ... @@ -1944,7 +2159,7 @@ You can stack callbacks on a single event by passing them as an array: ```ruby class Customer < ActiveRecord::Base has_many :orders, - :before_add => [:check_credit_limit, :calculate_shipping_charges] + before_add: [:check_credit_limit, :calculate_shipping_charges] def check_credit_limit(order) ... @@ -1966,7 +2181,7 @@ You're not limited to the functionality that Rails automatically builds into ass class Customer < ActiveRecord::Base has_many :orders do def find_by_order_prefix(order_number) - find_by_region_id(order_number[0..2]) + find_by(region_id: order_number[0..2]) end end end |