diff options
Diffstat (limited to 'guides/source/association_basics.md')
-rw-r--r-- | guides/source/association_basics.md | 353 |
1 files changed, 295 insertions, 58 deletions
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index 95adb4ff0a..a12f5638e3 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -40,7 +40,7 @@ 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 @@ -69,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` @@ -94,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, index: true + 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: @@ -106,6 +125,25 @@ end ![has_one Association Diagram](images/has_one.png) +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, index: true + 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: @@ -120,6 +158,25 @@ NOTE: The name of the other model is pluralized when declaring a `has_many` asso ![has_many Association Diagram](images/has_many.png) +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, index:true + 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: @@ -143,6 +200,31 @@ end ![has_many :through Association Diagram](images/has_many_through.png) +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, index: true + t.belongs_to :patient, index: true + 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 @@ -179,7 +261,10 @@ 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 @@ -199,6 +284,31 @@ end ![has_one :through Association Diagram](images/has_one_through.png) +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, index: true + t.string :account_number + t.timestamps + end + + create_table :account_histories do |t| + t.belongs_to :account, index: true + 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: @@ -215,6 +325,29 @@ end ![has_and_belongs_to_many Association Diagram](images/habtm.png) +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, index: true + t.belongs_to :part, index: true + 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? @@ -246,6 +379,8 @@ class CreateSuppliers < ActiveRecord::Migration t.string :account_number t.timestamps end + + add_index :accounts, :supplier_id end end ``` @@ -322,6 +457,8 @@ class CreatePictures < ActiveRecord::Migration t.string :imageable_type t.timestamps end + + add_index :pictures, :imageable_id end end ``` @@ -333,7 +470,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, index: true t.timestamps end end @@ -357,6 +494,19 @@ end With this setup, you can retrieve `@employee.subordinates` and `@employee.manager`. +In your migrations/schema, you will add a references column to the model itself. + +```ruby +class CreateEmployees < ActiveRecord::Migration + def change + create_table :employees do |t| + t.references :manager, index: true + t.timestamps + end + end +end +``` + Tips, Tricks, and Warnings -------------------------- @@ -415,6 +565,8 @@ class CreateOrders < ActiveRecord::Migration t.string :order_number t.integer :customer_id end + + add_index :orders, :customer_id end end ``` @@ -425,7 +577,7 @@ If you create an association some time after you build the underlying model, you If you create a `has_and_belongs_to_many` association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the `:join_table` option, Active Record creates the name by using the lexical order of the class names. So a join between customer and order models will give the default join table name of "customers_orders" because "c" outranks "o" in lexical ordering. -WARNING: The precedence between model names is calculated using the `<` operator for `String`. This means that if the strings are of different lengths, and the strings are equal when compared up to the shortest length, then the longer string is considered of higher lexical precedence than the shorter one. For example, one would expect the tables "paper\_boxes" and "papers" to generate a join table name of "papers\_paper\_boxes" because of the length of the name "paper\_boxes", but it in fact generates a join table name of "paper\_boxes\_papers" (because the underscore '\_' is lexicographically _less_ than 's' in common encodings). +WARNING: The precedence between model names is calculated using the `<` operator for `String`. This means that if the strings are of different lengths, and the strings are equal when compared up to the shortest length, then the longer string is considered of higher lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers" to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes", but it in fact generates a join table name of "paper_boxes_papers" (because the underscore '_' is lexicographically _less_ than 's' in common encodings). Whatever the name, you must manually generate the join table with an appropriate migration. For example, consider these associations: @@ -442,12 +594,15 @@ 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| t.integer :assembly_id t.integer :part_id end + + add_index :assemblies_parts, :assembly_id + add_index :assemblies_parts, :part_id end end ``` @@ -563,6 +718,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 ------------------------------ @@ -574,12 +740,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: @@ -589,13 +756,14 @@ class Order < ActiveRecord::Base end ``` -Each instance of the order model will have these methods: +Each instance of the `Order` model will have these methods: ```ruby 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. @@ -636,6 +804,10 @@ The `create_association` method returns a new object of the associated type. Thi 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` @@ -714,8 +886,12 @@ end Counter cache columns are added to the containing model's list of read-only attributes through `attr_readonly`. ##### `:dependent` +If you set the `:dependent` option to: -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. +* `:destroy`, when the object is destroyed, `destroy` will be called on its +associated objects. +* `:delete`, when the object is destroyed, all its associated objects will be +deleted directly from the database without calling their `destroy` method. 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. @@ -806,7 +982,7 @@ 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 @@ -872,12 +1048,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: @@ -894,6 +1071,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. @@ -932,6 +1110,10 @@ The `create_association` method returns a new object of the associated type. Thi @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 association uses two such options: @@ -958,7 +1140,7 @@ The `has_one` association supports these options: ##### `:as` -Setting the `:as` option indicates that this is a polymorphic association. Polymorphic associations were discussed in detail <a href="#polymorphic-associations">earlier in this guide</a>. +Setting the `:as` option indicates that this is a polymorphic association. Polymorphic associations were discussed in detail [earlier in this guide](#polymorphic-associations). ##### `:autosave` @@ -979,11 +1161,17 @@ 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: @@ -1024,7 +1212,7 @@ The `:source_type` option specifies the source association type for a `has_one : ##### `:through` -The `:through` option specifies a join model through which to perform the query. `has_one :through` associations were discussed in detail <a href="#the-has-one-through-association">earlier in this guide</a>. +The `:through` option specifies a join model through which to perform the query. `has_one :through` associations were discussed in detail [earlier in this guide](#the-has-one-through-association). ##### `:validate` @@ -1127,15 +1315,15 @@ 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, ...)` * `collection.delete(object, ...)` * `collection.destroy(object, ...)` -* `collection=objects` +* `collection=(objects)` * `collection_singular_ids` -* `collection_singular_ids=ids` +* `collection_singular_ids=(ids)` * `collection.clear` * `collection.empty?` * `collection.size` @@ -1144,6 +1332,7 @@ 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: @@ -1153,16 +1342,16 @@ class Customer < ActiveRecord::Base end ``` -Each instance of the customer model will have these methods: +Each instance of the `Customer` model will have these methods: ```ruby orders(force_reload = false) orders<<(object, ...) orders.delete(object, ...) orders.destroy(object, ...) -orders=objects +orders=(objects) order_ids -order_ids=ids +order_ids=(ids) orders.clear orders.empty? orders.size @@ -1171,6 +1360,7 @@ orders.where(...) orders.exists?(...) orders.build(attributes = {}, ...) orders.create(attributes = {}) +orders.create!(attributes = {}) ``` ##### `collection(force_reload = false)` @@ -1209,7 +1399,7 @@ The `collection.destroy` method removes one or more objects from the collection WARNING: Objects will _always_ be removed from the database, ignoring the `:dependent` option. -##### `collection=objects` +##### `collection=(objects)` The `collection=` method makes the collection contain only the supplied objects, by adding and deleting as appropriate. @@ -1221,7 +1411,7 @@ The `collection_singular_ids` method returns an array of the ids of the objects @order_ids = @customer.order_ids ``` -##### `collection_singular_ids=ids` +##### `collection_singular_ids=(ids)` The `collection_singular_ids=` method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate. @@ -1286,6 +1476,10 @@ The `collection.create` method returns a new object of the associated type. This 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 association uses two such options: @@ -1312,7 +1506,7 @@ The `has_many` association supports these options: ##### `:as` -Setting the `:as` option indicates that this is a polymorphic association, as discussed <a href="#polymorphic-associations">earlier in this guide</a>. +Setting the `:as` option indicates that this is a polymorphic association, as discussed [earlier in this guide](#polymorphic-associations). ##### `:autosave` @@ -1333,13 +1527,11 @@ 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 -NOTE: This option is ignored when you use the `:through` option on the association. - ##### `: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: @@ -1370,6 +1562,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. @@ -1380,7 +1586,7 @@ The `:source_type` option specifies the source association type for a `has_many ##### `:through` -The `:through` option specifies a join model through which to perform the query. `has_many :through` associations provide a way to implement many-to-many relationships, as discussed <a href="#the-has-many-through-association">earlier in this guide</a>. +The `:through` option specifies a join model through which to perform the query. `has_many :through` associations provide a way to implement many-to-many relationships, as discussed [earlier in this guide](#the-has-many-through-association). ##### `:validate` @@ -1433,7 +1639,7 @@ If you use a hash-style `where` option, then record creation via this associatio ##### `extending` -The `extending` method specifies a named module to extend the association proxy. Association extensions are discussed in detail <a href="#association-extensions">later in this guide</a>. +The `extending` method specifies a named module to extend the association proxy. Association extensions are discussed in detail [later in this guide](#association-extensions). ##### `group` @@ -1518,43 +1724,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 :articles, through: :readings end 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>] +article = Article.create(name: 'a1') +person.articles << article +person.articles << article +person.articles.inspect # => [#<Article id: 5, name: "a1">, #<Article id: 5, name: "a1">] +Reading.all.inspect # => [#<Reading id: 12, person_id: 5, article_id: 5>, #<Reading id: 13, person_id: 5, article_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.articles` brings out both of +them even though these records are pointing to the same article. -Now let's set `uniq`: +Now let's set `distinct`: ```ruby class Person has_many :readings - has_many :posts, -> { uniq }, through: :readings + has_many :articles, -> { distinct }, through: :readings end 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>] +article = Article.create(name: 'a1') +person.articles << article +person.articles << article +person.articles.inspect # => [#<Article id: 7, name: "a1">] +Reading.all.inspect # => [#<Reading id: 16, person_id: 7, article_id: 7>, #<Reading id: 17, person_id: 7, article_id: 7>] +``` + +In the above case there are still two readings. However `person.articles` shows +only one article 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_articles` and you want to make sure all the articles are unique, you could +add the following in a migration: + +```ruby +add_index :person_articles, :article, unique: true ``` -In the above case there are still two readings. However `person.posts` shows only one post because the collection loads only unique records. +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 article example from above, the +following code would be racy because multiple users could be attempting this +at the same time: + +```ruby +person.articles << article unless person.articles.include?(article) +``` #### When are Objects Saved? @@ -1572,15 +1802,15 @@ 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, ...)` * `collection.delete(object, ...)` * `collection.destroy(object, ...)` -* `collection=objects` +* `collection=(objects)` * `collection_singular_ids` -* `collection_singular_ids=ids` +* `collection_singular_ids=(ids)` * `collection.clear` * `collection.empty?` * `collection.size` @@ -1589,6 +1819,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: @@ -1598,16 +1829,16 @@ class Part < ActiveRecord::Base end ``` -Each instance of the part model will have these methods: +Each instance of the `Part` model will have these methods: ```ruby assemblies(force_reload = false) assemblies<<(object, ...) assemblies.delete(object, ...) assemblies.destroy(object, ...) -assemblies=objects +assemblies=(objects) assembly_ids -assembly_ids=ids +assembly_ids=(ids) assemblies.clear assemblies.empty? assemblies.size @@ -1616,6 +1847,7 @@ assemblies.where(...) assemblies.exists?(...) assemblies.build(attributes = {}, ...) assemblies.create(attributes = {}) +assemblies.create!(attributes = {}) ``` ##### Additional Column Methods @@ -1661,7 +1893,7 @@ The `collection.destroy` method removes one or more objects from the collection @part.assemblies.destroy(@assembly1) ``` -##### `collection=objects` +##### `collection=(objects)` The `collection=` method makes the collection contain only the supplied objects, by adding and deleting as appropriate. @@ -1673,7 +1905,7 @@ The `collection_singular_ids` method returns an array of the ids of the objects @assembly_ids = @part.assembly_ids ``` -##### `collection_singular_ids=ids` +##### `collection_singular_ids=(ids)` The `collection_singular_ids=` method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate. @@ -1735,14 +1967,18 @@ The `collection.create` method returns a new object of the associated type. This @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 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 +1990,7 @@ The `has_and_belongs_to_many` association supports these options: * `:foreign_key` * `:join_table` * `:validate` +* `:readonly` ##### `:association_foreign_key` @@ -1763,7 +2000,7 @@ TIP: The `:foreign_key` and `:association_foreign_key` options are useful when s ```ruby class User < ActiveRecord::Base - has_and_belongs_to_many :friends, + has_and_belongs_to_many :friends, class_name: "User", foreign_key: "this_user_id", association_foreign_key: "other_user_id" @@ -1852,7 +2089,7 @@ If you use a hash-style `where`, then record creation via this association will ##### `extending` -The `extending` method specifies a named module to extend the association proxy. Association extensions are discussed in detail <a href="#association-extensions">later in this guide</a>. +The `extending` method specifies a named module to extend the association proxy. Association extensions are discussed in detail [later in this guide](#association-extensions). ##### `group` @@ -1968,7 +2205,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 |