<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 4.3 模型中的關聯關系(Relations) ## 概要: 本課時講解 Rails 中 Model 和 Model 間的關聯關系。 ## 知識點: 1. belongs_to 1. has_one 1. has_many 1. has_and_belongs_to_many 1. self join ## 正文 ### 導讀 如果你對一對一關系,一對多關系,多對多關系并不十分了解的話,或者你對關系型數據庫并不十分了解的話,建議你在閱讀下面的內容前,先熟悉一下相關內容。因為我并不想照本宣科的講解手冊。我想講的,是對它的理解,并且把我們的精力,放到設計我們的商城中。 本章涉及的知識,可以查看 [Active Record Associations](http://guides.rubyonrails.org/association_basics.html),或者 [ActiveRecord::Associations::ClassMethods](http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html)。 接下來的內容,希望能幫助你理解模型間的關聯關系。 ### 4.3.1 模型間的關系 在前面的章節里,我們為商城設計了界面,并且使用了3個 model: 1. User,網站用戶,使用 devise 提供了用戶注冊,登錄功能。 1. Product,商品 1. Variant,商品類型 我們在前面講解的過程中,已經提到了 Product 和 Variant 的關系。一個 Product 有多個 Variant。現在我們需要增加幾個模型,模型是根據功能來的,我們的網店要增加哪些功能呢? - 當用戶購買實物商品的時候,我們是要輸入它的收貨地址(Address)。 - 當用戶選擇商品的時候,選擇不同的顏色和大小,會有不同的價格(Variant)。 - 我們點擊購買,會創建一個購物訂單(Order),上面有我們選擇的商品,應支付的金額,和訂單的狀態。 - 查看用戶購買的商品類型 在我們的網店里,一個 User 有一個地址,每次購物的時候,會讀取這個地址作為送貨地址。 一個 Product 有多個 Variant,每個 Variant 保存它的顏色,大小等屬性。 一個用戶會有多個訂單 Order,每個訂單會顯示購買的商品 Product,以及多條購買記錄,每條記錄顯示購買的 Variant 的每個數量和應付的價格,這里我們使用 LineItem 表示訂單的訂單項。 ### 4.3.2 外鍵 兩個 model 之間,通過外鍵進行關聯,Rails 中默認的外鍵名稱是所屬 model 的 `名稱_id`,比如,User 有一條 Address 記錄,那么 addresses 表上,需要增加一個數字類型的字段 `user_id`。而 User 的主鍵通常為 id 字段。有一些遺留的數據庫,使用的外鍵可能不是按照 Rails 默認的格式,所以在聲明外鍵關聯時,需要指定 `foreign_key`。 在我們創建 Model 的時候,可以在 generate 命令上增加外鍵關聯,我們現在創建 Address 這個 Model ~~~ rails g model address user:references state city address address2 zipcode receiver phone ~~~ 在創建的 migration 文件中: ~~~ create_table :addresses do |t| t.references :user, index: true, foreign_key: true ~~~ 自動增加了外鍵關聯,并且將 user_id 加入索引。如果是更改其他數據庫,需要在 migration 文件內單獨設置索引: ~~~ add_index "addresses", ["user_id"], name: "index_addresses_on_user_id" ~~~ 模型間的關系,都是通過外鍵實現的,下面我們詳細介紹模型間的關系,并且實現我們商城的 Model。 ### 4.3.3 一對一關系 一對一關系的設定,再一次體現了 Rails 在開發中的便捷: ~~~ class User < ActiveRecord::Base has_one :address end class Address < ActiveRecord::Base belongs_to :user end ~~~ 在一對一關系中,`belongs_to :user` 中,`:user` 是單數,`has_one :address` 中,`:address` 也是單數。 我們進入到 console 里來測試一下: ~~~ user = User.first user.address => nil ~~~ #### 4.3.3.1 新建子資源 如何為 user 保存 address 呢? 一種是使用 Address 的類方法 `create`: ~~~ Address.create(user_id: user.id, ...) ~~~ 我們也可以省去 id 的寫法,直接寫上所屬的實例: ~~~ Address.create(user: user, ...) ~~~ 一種是使用實例方法: ~~~ address = Address.new address.user = user address.save ~~~ 或者: ~~~ user.address = Address.create( ... ) ~~~ 這種方法會產生兩句 SQL,先是 insert 一個 address 到數據庫,然后更新它的 user_id 為剛才的 user。我們可以換一個方法: ~~~ user.address = Address.new( ... ) ~~~ 它只產生一條 insert SQL,并且會帶上 user_id 的值。 在創建關聯關系時,還有這樣的方法: ~~~ user.create_address( ... ) user.build_address( ... ) ~~~ build_xxx 相當于 Address.new。create_xxx也會產生兩條 SQL,每條 SQL 都包含在一個 transaction 中。 所以我們得出結論: 把一個未保存的實例,賦值給一對一關系時,它會自動保存,并且只有一條 sql 產生。 先 create 一個實例,再把賦值給一對一關系時,是先保存,再更新,產生兩條 sql。 #### 4.3.3.2 保存子資源 當我們編寫表單的時候,一個表單針對的是一個資源。當這個資源擁有(has_one 或 has_many)子資源時,我們可以在提交表單的時候,將它擁有的資源也保存到數據庫中。 這時,我們需要在 User中,做一個聲明: ~~~ class User < ActiveRecord::Base has_one :address accepts_nested_attributes_for :address end ~~~ `accepts_nested_attributes_for` 會為 User 增加一個新的方法 `address_attributes=(attributes)`,這樣,在創建 User 的 時候: ~~~ user_hash = { email: "test@123.com", password: "123456", password_confirmation: "123456", address_attributes: { receiver: "Some One", state: "Beijing", city: "Beijing", phone: "123456"} } u = User.create(user_hash) u.address ~~~ 只要保存 User 的時候,傳遞入 Address 的參數,就可以把關聯的 address 一并保存到數據庫中了。 更新記錄的時候,也可以使用同樣的方法: ~~~ user_hash = { email: "changed@123.com", address_attributes: { receiver: "Other One" } } user.update(user_hash) ~~~ 但是,這里要注意,上面的方法會把之前舊記錄的 user_id 設為 nil,然后插入一條新的記錄。這并不能真正起到更新的作用,除非所有屬性都重新復制,不然,新的 address 記錄只有 receiver 這個值。 我們在 accepts_nested_attributes_for 后增加一個參數: ~~~ accepts_nested_attributes_for :address, update_only: true ~~~ 這樣,update 時候會更新已有的記錄。 如果我們不能增加 `update_only` 屬性,為了避免創建無用的記錄,需要在 hash 里指定子資源的 id: ~~~ user_hash = { email: "changed@123.com", address_attributes: { id: 1, receiver: "Other One" } } user.update(user_hash) ~~~ #### 4.3.3.3 使用表單保存子資源 `accepts_nested_attributes_for` 方法,在 Form 中有其對應的方法: ~~~ <%= f.fields_for :address do |address_form| %> <%= address_form.hidden_field :id unless resource.new_record? %> <div class="form-group"> <%= address_form.label :state, class: "control-label" %><br /> <%= address_form.text_field :state, class: "form-control" %> </div> ... <% end %> ~~~ 打開 [代碼](https://github.com/liwei78/rails-practice-code/blob/master/chapter_4/shop/app/views/devise/registrations/edit.html.erb#L32),在編輯一個用戶的時候,我為它增加了一個 `f.fields_for` 的子表單,對應了子資源的屬性。 我想,這段代碼這并不難理解,不過我們用了 Devise 這個 gem,還需要做一點額外的處理。 打開 application_controller.rb,我們需要讓 devise 支持傳進來新增的參數: ~~~ class ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:email, :password, :password_confirmation, :address_attributes) } devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:email, :password, :password_confirmation, :current_password, address_attributes: [:state, :city, :address, :address2, :zipcode, :receiver, :phone] ) } end end ~~~ 在我們注冊賬號的時候,并沒有創建 address ,但是在編輯的時候,因為它是 nil,所以不會顯示這個子表單,所以我們需要在編輯的時候創建一個空的 address: `views/devise/registrations/edit.html.erb` ~~~ <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> <% resource.build_address if resource.address.nil? %> ... ~~~ 當然,我們也可以在注冊的時候提供地址表單,大家不妨一試。 #### 4.3.3.4 刪除關聯的子資源 在上一節里,我們介紹了 delete 和 destroy 方法,我們可以使用這兩個方法把關聯的 address 刪除掉: ~~~ u.address.delete SQL (10.0ms) DELETE FROM "addresses" WHERE "addresses"."id" = ? [["id", 2]] ~~~ 或者: ~~~ u.address.destroy (0.1ms) begin transaction SQL (0.7ms) DELETE FROM "addresses" WHERE "addresses"."id" = ? [["id", 3]] (9.2ms) commit transaction ~~~ 兩者的區別在上一節介紹過,我們注意到,delete 直接發送數據庫刪除命令,而 destroy 會將刪除命令放置到一個 sql 的事物中,因為它會觸發模型中的回調,如果回調拋出異常,刪除動作會失敗。 #### 4.3.3.5 刪除自身同時刪除關聯的子資源 在刪除某個資源的時候,我們想把它擁有的資源一并刪除,這時,我們需要給 has_one 方法,增加一個參數: ~~~ has_one :address, dependent: :destroy ~~~ dependent 可以接收五個參數: | 參數 | 含義 | |-----|-----| | :destroy | 刪除擁有的資源 | | :delete | 直接發送刪除命令,不會執行回調 | | :nullify | 將擁有的資源外鍵設為 null | | :restrict_with_exception | 如果擁有資源,會拋出異常,也就是說,當它 has_one 為 nil 的時候,才能正常刪除它自己 | | :restrict_with_error | 如有擁有資源,會增加一個 errors 信息。 | 在 belongs_to 上,也可以設置 dependent,但它只有兩個參數: | 參數 | 含義 | |-----|-----| | :destroy | 刪除它所屬的資源 | | :delete | 刪除它所屬的資源,直接發送刪除命令,不會執行回調 | 兩種設定,出發角度是不同的,不過,刪除本身的同時刪除上層資源是比較危險的,需謹慎。 #### 4.3.3.6 失去關聯關系的子資源 如果在 has_one 中設置了 `dependent: :destroy` 或 `dependent: :delete`,當子資源失去該關聯關系時,它也會被刪除。 ~~~ user.address = nil ~~~ 如果不設置,一個子資源失去關系時,外鍵設置為 null。 #### 4.3.3.7 子資源維護 當一個子資源失去關聯關系,和它在關聯關系中被刪除,是一樣的。我們在設計時,應盡量避免產生孤立的記錄,這些記錄外鍵為 null,或者所屬的資源已經被刪除,他們是無意義的存在。 ### 4.3.4 一對多關系 在電商系統里,一個用戶是有多個訂單(Order)的,User 中使用的是 has_many 方法: ~~~ class User < ActiveRecord::Base has_many :orders end ~~~ 除了名稱變為復數形式,返回的結果是數組,其他情形和“一對一”是一樣的。 我們使用 generate 創建 Order: ~~~ rails g model order user:references number payment_state shipment_state ~~~ number 是訂單的唯一編號,payment_state 是付款狀態,shipment_state 是發貨狀態。 payment_state 的狀態順序是:pending(等待支付),paid(已支付)。 shipment_state 的狀態順序是:pending(等待發貨),shipped(已發貨)。 這兩種狀態,我們只做簡單的設計,實際中要復雜得多。 開源電商程序 [spree](https://spreecommerce.com/) 是一套很好的在線交易程序,因為其開源,其中的概念和定義對開發電商程序有很好的啟發。它的源代碼在 [這里](https://github.com/spree/spree),目前是最新版本是 3.0.2.beta。 #### 4.3.4.1 添加子資源 一對多關系返回的,是 [CollectionProxy](http://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html) 實例。 當添加一對多關系時,可以很“形象”的使用: ~~~ product.variants << Variant.new product.variants << [Variant.new, Variant.new] ~~~ 執行 `<<` 的時候,variant 的 product_id 會自動保存為 product.id。 如果 variant 是一個未保存到數據庫的實例,<< 執行的時候會自動將它保存,并且賦予它 product_id 值。這是一步完成的,只有一條 SQL。 但是,如果是下面的情形: ~~~ product.variants << Variant.create ~~~ 會把 variant 先保存到數據庫,然后再更新它的 product_id 字段,這會產生兩條 SQL。 這里也可以使用 build 方法,和上面“一對一關系”不同的是,它需要在 collection 上執行: ~~~ variant = product.variants.build( ... ) variant.save ~~~ build 返回的是一個未保存的實例。查看 `product.variants`,會看到它包含了一個未保存的 variant(ID 為 nil)。 另一種情形: ~~~ product.variants.build( ... ) product.save ~~~ 當這個 product.save 的時候,這個 variant 也會保存到數據庫中。 #### 4.3.4.2 刪除子資源 刪除資源的時候,可以使用幾個方法: ~~~ product.variants.delete(...) product.variants.destroy(...) product.variants.clear ~~~ delete 不會真正刪除掉資源,而是把它的外鍵(product_id)設為 nil,而 destroy 會真正的刪除掉它并出發回調。 他們都可以傳遞進一個實例,或者實例的集合,而并不管這個實例是否真的屬于它。 ~~~ product.variants.delete(Variant.find(1)) product.variants.delete(Variant.find(1,2,3)) ~~~ 這樣是不是太霸道了?所以,建議用第三個方法更穩妥些。clear 方法會把外鍵置為 nil。 如果再 has_many 上聲明了 `dependent: :destroy`,會用 destroy 方式把它們刪除(有回調)。如果聲明的是 `dependent: :delete_all`,會用 delete 方法(跳過回調)。這和一對一中描述是一致的。 注意: has_many 和 has_one 上的 dependent 選項,適用以下兩種情形: - 刪除自身時,如何處理子資源 - 當子資源失去該關聯關系時,如何處理該子資源 我們來看下一節。 #### 4.3.4.3 更改子資源 當改動關系的時候,可以直接使用 `=`,假設我們有 ID 為 1,2,3,4 的 Variant: ~~~ product.variants = Variant.find(1,2) ~~~ 這時會自動把 ID:1,ID:2 的 product_id 外鍵設為 null。 再次選擇 ID:3,ID:4 的 variant: ~~~ product.variants = Variant.find(3,4) ~~~ 會自動把 ID:3,ID:4 的 product_id 外鍵設置為 product.id。 如果在 has_many 設置了 `dependent: :destroy`,當 UD:1 和 ID:2 失去關聯的時候,會把它們從數據庫中刪除掉。這與 has_one 中的 dependent 選項是一樣的。詳見本章前面 `4.3.3.4 刪除自身同時刪除關聯的子資源`。 #### 4.3.4.4 counter_cache “一對多”關系中,`belongs_to` 方法可以增加 counter_cache 屬性: ~~~ class Order < ActiveRecord::Base belongs_to :user, counter_cache: true end ~~~ 這時,我們需要給 users 表增加一個字段:orders_count,當我們把一個 order 保存到一對多的關系中時,orders_count 會自動 +1,當把一個資源從關系中刪除,該字段會 -1。如此我們不必去增加計算一個 user 有多少個 orders,只需要讀該字段就可以了。 向 Users 表添加 orders_count 字段: ~~~ rails g migration add_orders_count_to_users orders_count:integer ~~~ #### 4.3.4.5 多態 當一個資源可能屬于多種資源時,可以用到多態。舉個栗子: 商品可以評論,文章可以評論,而評論 model 對任何一個資源都是一樣的功能,所以,評論在 belongs_to 的后面,增加: ~~~ class Comment < ActiveRecord::Base belongs_to :commentable, polymorphic: true end ~~~ Comment 的遷移文件,也相應的增加設定: ~~~ t.references :commentable, polymorphic: true, index: true ~~~ 如果是手動添加字段,需要這樣來寫: ~~~ t.string :commentable_type t.integer :commentable_id ~~~ 說明,查找一個多態資源時,是根據擁有者的類型(type,一般是它的類名稱)和 ID 進行匹配的。 擁有評論的 model,也需要改動下: ~~~ class Product < ActiveRecord::Base has_many :commentable, as: :commentable end class Topic < ActiveRecord::Base has_many :commentable, as: :commentable end ~~~ 多態并不局限于一對多關系,一對一也同樣適用。 ### 4.3.5 中間模型和中間表 has_one 和 has_many,是兩個 model 間的操作。我們可以增加一個中間模型,描述之前兩個 model間的關系。 ### 4.3.5.1 中間模型 我們先創建訂單項(LineItem)這個 model,它屬于一個訂單,也屬于一個商品類型(Variant)。 ~~~ rails g model line_item order:references variant:references quantity:integer ~~~ 對于一個訂單,我們有多個訂單項,對于一個訂單項,會關聯購買的具體商品類型,那么,一個訂單擁有的商品類型,就可以通過 through 查找到。 ~~~ class Order < ActiveRecord::Base belongs_to :user, counter_cache: true has_many :line_items has_many :variants, through: :line_items end ~~~ ~~~ class LineItem < ActiveRecord::Base belongs_to :order belongs_to :variant end ~~~ 我們進到終端里進行查找: ~~~ order = Order.first order.variants => SELECT "variants".* FROM "variants" INNER JOIN "line_items" ON "variants"."id" = "line_items"."variant_id" WHERE "line_items"."order_id" = ? [["order_id", 1]] => #<ActiveRecord::Associations::CollectionProxy []> ~~~ 可以看到,through 為使用了 `inner join` 的 sql 語法。 LineItem 是兩個模型,Order 和 Variant 的中間模型,它表示訂單中的每一項。但是,中間模型不一定要使用兩個 `belongs_to` 連接兩邊的模型,比如: ~~~ class User < ActiveRecord::Base has_many :orders has_many :line_items, through: :orders end ~~~ 進到終端,我們查看一個用戶有哪些訂單項: ~~~ user = User.first user.line_items => SELECT "line_items".* FROM "line_items" INNER JOIN "orders" ON "line_items"."order_id" = "orders"."id" WHERE "orders"."user_id" = ? [["user_id", 1]] ~~~ 從左邊可以查到右邊資源,那么,可以通過中間表,從右邊查找左邊資源么? 我們給 Variant 增加關聯: ~~~ class Variant < ActiveRecord::Base belongs_to :product has_many :line_item has_many :orders, through: :line_item end ~~~ 進入終端: ~~~ v = Variant.last v.orders => SELECT "orders".* FROM "orders" INNER JOIN "line_items" ON "orders"."id" = "line_items"."order_id" WHERE "line_items"."variant_id" = ? [["variant_id", 2]] ~~~ 因為中間表 LineItem 擁有兩邊的外鍵,所以可以查找 variant 的 orders。但是 orders 上沒有 line_item_id 字段,因為這不符合我們的業務邏輯,所以無法查找 line_item.user。如果需要查找,可以給 line_item 上增加 user_id 字段。 ~~~ class LineItem < ActiveRecord::Base belongs_to :order belongs_to :variant belongs_to :user end ~~~ ### 4.3.5.2 中間表 中間模型的作用,除了連接兩端模型外,更重要的是,它保存了業務中屬于中間模型的數據,比如,訂單項中的 quantity 字段。如果模型不必或者沒有這種字段,可以不用增加 model,而直接使用中間表。 我們有一個功能:保存用戶購買的商品類型。這時可以使用中間表,保存購買關系。 中間表具有兩端模型的外鍵。兩端模型使用 `has_and_belongs_to_many` 方法(簡寫:HABTM)。 在創建中間表的時候,也可以使用 migration,如果在表名中包含 `JoinTable` 字樣,會自動創建中間表: ~~~ rails g migration CreateJoinTable users variants:uniq ~~~ 運行 `rake db:migrate`,查看 schema.rb: ~~~ create_table "users_variants", id: false, force: :cascade do |t| t.integer "user_id", null: false t.integer "variant_id", null: false end add_index "users_variants", ["variant_id", "user_id"], name: "index_users_variants_on_variant_id_and_user_id", unique: true ~~~ 調整一下 User 和 Variant model: ~~~ class User < ActiveRecord::Base ... has_and_belongs_to_many :variants end class Variant < ActiveRecord::Base ... has_and_belongs_to_many :users end ~~~ 在終端里測試: ~~~ user.variants => SELECT "variants".* FROM "variants" INNER JOIN "users_variants" ON "variants"."id" = "users_variants"."variant_id" WHERE "users_variants"."user_id" = ? [["user_id", 1]] variant.users => SELECT "users".* FROM "users" INNER JOIN "users_variants" ON "users"."id" = "users_variants"."user_id" WHERE "users_variants"."variant_id" = ? [["variant_id", 2]] ~~~ 利用中間表,實現了多對多關系。 ### 4.3.5.3 多對多關系 查看一個用戶購買了哪些商品類型,和查看一個商品類型被哪些用戶購買,這就是多對多關系。 保存和刪除多對多關系,和一對多關系的操作是一樣的。因為我們在創建 migration 時,增加了索引唯一校驗,在操作時要做好異常處理,或者保存前進行判斷。 ~~~ user.variants << variant user.variants << variant => SQLite3::ConstraintException: columns variant_id, user_id are not unique: ... ~~~ ### 4.3.5.4 inner join ActiveRecord 在查詢關聯關系時,使用的是 inner join 查詢,我們可以單獨使用 `join` 方法,實現該查詢。 比如,一個簡單的 join 查詢: ~~~ % Order.joins(:line_items) => SELECT "orders".* FROM "orders" INNER JOIN "line_items" ON "line_items"."order_id" = "orders"."id" ~~~ 也可以查詢多個關聯的: ~~~ % Order.joins(:line_items, :user) => SELECT "orders".* FROM "orders" INNER JOIN "line_items" ON "line_items"."order_id" = "orders"."id" INNER JOIN "users" ON "users"."id" = "orders"."user_id" ~~~ 或者嵌套關聯: ~~~ % Order.joins(line_items: [:variant]) => SELECT "orders".* FROM "orders" INNER JOIN "line_items" ON "line_items"."order_id" = "orders"."id" INNER JOIN "variants" ON "variants"."id" = "line_items"."variant_id" ~~~ 但是,在一些更復雜的查詢中,我們需要改變 `inner join` 查詢為 `left join` 或 `right join`: ~~~ User.select("users.*, orders.*").joins("LEFT JOIN `orders` ON orders.user_id = users.id") ~~~ 這時返回的是全部用戶,即便它沒有訂單。這在生成一些報表時是有用的。 ### 4.3.6 自連接 在設計模型的時候,一個模型即可以是 Catalog(類別),也可以是 Subcatalog(子類別),我們為網店添加 `類別` Model: ~~~ rails g model catalog parent_catalog:references name parent:boolean ~~~ 看一下 catalog.rb: ~~~ class Catalog < ActiveRecord::Base has_many :subcatalogs, class_name: "Catalog", foreign_key: "parent_catalog_id" belongs_to :parent_catalog, class_name: "Catalog" has_many :products end ~~~ 這樣,我們可以實現分類,也可以吧商品加入到某個分類中。 ### 4.3.7 雙向關聯 我們查找關聯關系的時候,是可以在兩邊同時查找,比如: ~~~ class User < ActiveRecord::Base has_one :address end class Address < ActiveRecord::Base belongs_to :user end ~~~ 我們可以 `user.address`,也可以 `address.user`,這叫做 Bi-directional,雙向關聯。(和它相反,Uni-directional,單向關聯) 但是,這在我們的內存查找中,會引起問題: ~~~ u = User.first a = u.address u.email == a.user.email => true u.email = "a@1.com" u.email == a.user.email => false ~~~ 原因是: ~~~ u.object_id => 70241969456560 a.user.object_id => 70241969637580 ~~~ 兩個類并不是在內存中指向同一個地址,他們是不同的兩個類。 為了避免這個問題,我們需要使用 inverse_of: ~~~ class User < ActiveRecord::Base has_one :address, inverse_of: :user end class Address < ActiveRecord::Base belongs_to :user, inverse_of: :address end ~~~ 當 model 的關聯關系上,已經有 polymorphic,through,as 時,可以不用加 inverse_of,它自然會指向同一個 object,大家可以使用 user 和 order 之間的關聯驗證。對于 user 和 address 之間,還是應該加上 inverse_of 選項。 ### 4.3.8 Rspec測試 關聯關系的測試,可以使用 [shoulda-matchers](https://github.com/thoughtbot/shoulda-matchers) 這個 gem。它為 Rails 的模型間關聯提供了方便的測試方法。 比如: ~~~ RSpec.describe User, type: :model do it { should have_many(:orders) } end RSpec.describe Order, type: :model do it { should belong_to(:user) } end ~~~ 更多模型間關聯關系測試的方法,可以查看 [ActiveRecord matchers](https://github.com/thoughtbot/shoulda-matchers#activerecord-matchers)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看