User/Micropost associations
為 web APP 建立資料模型(data models)時,最基本的需求就是能夠在不同的模型之間建立關聯(associations)。在我們的例子中,每一個 micropost 會屬於某個使用者,而每個使用者則會擁有多篇 microposts,在實現這些關聯的過程中,我們會先為 Micropost model 和 User model 撰寫測試。
microposts 和所屬使用者之間的 belongs_to (屬於)關係

使用者和 microposts 之間的 has_many (擁有多個)關係

使用本節實現的 belongs_to/has_many 關聯後,Rails 會自動建立一些方法,如下表:
| Method | Purpose |
|---|---|
| micropost.user | Returns the User object associated with the micropost |
| user.microposts | Returns a collection of the user’s microposts |
| user.microposts.create(arg) | Creates a micropost associated with user |
| user.microposts.create!(arg) | Creates a micropost associated with user (exception on failure) |
| user.microposts.build(arg) | Returns a new Micropost object associated with user |
| user.microposts.find_by(id: 1) | Finds the micropost with id 1 and user_id equal to user.id |
注意,從表中得知,相較於下面這些方法:
Micropost.create
Micropost.create!
Micropost.new
我們得到了:
user.microposts.create
user.microposts.create!
user.microposts.build
後者才是建立 micropost 的正確方式,即透過關聯的使用者建立。透過這種方式建立的 micropost,user_id 會自動設為正確的值,所以我們可以把以下程式碼:
@user = users(:michael)
# This code is not idiomatically correct.
@micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id)
改成:
@user = users(:michael)
@micropost = @user.microposts.build(content: "Lorem ipsum")
跟 new 方法一樣,build 會回傳一個儲存在記憶體內的物件,不會修改資料庫。一旦我們定義了正確的關聯,@micropost 變數就會自動把 user_id 屬性設成與之關聯的 user id。
為了讓 @user.microposts.build 能夠運作,我們需要更新 User model 和 Micropost model 的程式碼,讓它們產生關聯。其中一個在建立遷移時已經自動幫我們產生,也就是 belongs_to :user,還有一個 has_many :microposts 必須手動增加:
app/models/micropost.rb
class Micropost < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
end
app/models/user.rb
class User < ActiveRecord::Base
has_many :microposts
.
.
.
end
建立兩個資料模型之間的關聯後,我們就可以修改測試裡的 setup 方法:
test/models/micropost_test.rb
require 'test_helper'
class MicropostTest < ActiveSupport::TestCase
def setup
@user = users(:michael)
@micropost = @user.microposts.build(content: "Lorem ipsum")
end
test "should be valid" do
assert @micropost.valid?
end
test "user id should be present" do
@micropost.user_id = nil
assert_not @micropost.valid?
end
.
.
.
end
當然,經過微小的重構測試之後,現在應該會通過(Green):
$ bundle exec rake test