Active Record
Active Record 物件不會直接指定其屬性,而是從與其連結的資料表定義中推論。在資料庫中直接新增、移除和變更屬性及其類型。任何變更都會立即反映在 Active Record 物件中。在大部分常見情況下,將特定 Active Record 類別繫結到特定資料表的對應會自動進行,但對於不常見的情況,可以覆寫對應。
請參閱 table_name 中的對應規則,以及 files/activerecord/README_rdoc.html 中的完整範例,以深入了解。
建立
Active Record 接受建構函數參數,形式為雜湊或區塊。當您從其他地方(例如 HTTP 要求)接收資料時,雜湊方法特別有用。其運作方式如下
user = User.new(name: "David", occupation: "Code Artist")
user.name # => "David"
您也可以使用區塊初始化
user = User.new do |u|
u.name = "David"
u.occupation = "Code Artist"
end
當然,您也可以只建立一個空物件,然後在之後指定屬性
user = User.new
user.name = "David"
user.occupation = "Code Artist"
條件
條件可以指定為字串、陣列或雜湊,代表 SQL 陳述式的 WHERE 部分。當條件輸入受到污染且需要清除時,應使用陣列形式。當陳述式不包含污染資料時,可以使用字串形式。雜湊形式的運作方式與陣列形式非常類似,但只能使用等號和範圍。範例
class User < ActiveRecord::Base
def self.authenticate_unsafely(user_name, password)
where("user_name = '#{user_name}' AND password = '#{password}'").first
end
def self.authenticate_safely(user_name, password)
where("user_name = ? AND password = ?", user_name, password).first
end
def self.authenticate_safely_simply(user_name, password)
where(user_name: user_name, password: password).first
end
end
authenticate_unsafely
方法會將參數直接插入查詢中,因此如果 user_name
和 password
參數直接來自 HTTP 要求,則容易受到 SQL 注入攻擊。authenticate_safely
和 authenticate_safely_simply
都會在將 user_name
和 password
插入查詢之前清除它們,這將確保攻擊者無法跳脫查詢並偽造登入(或更糟)。
當在條件中使用多個參數時,很容易難以確切了解第四個或第五個問號代表什麼。在這種情況下,您可以改用命名繫結變數。這可透過將問號替換為符號,並提供一個雜湊,其中包含與符號金鑰相符的值
Company.where(
"id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
{ id: 3, name: "37signals", division: "First", accounting_date: '2005-01-01' }
).first
類似地,沒有陳述式的簡單雜湊會根據等號和 SQL AND 運算子產生條件。例如
Student.where(first_name: "Harvey", status: 1)
Student.where(params[:student])
雜湊中可以使用範圍來使用 SQL BETWEEN 運算子
Student.where(grade: 9..12)
陣列可以用在雜湊中,以使用 SQL IN 運算子
Student.where(grade: [9,11,12])
當連接表格時,巢狀雜湊或寫成「table_name.column_name」形式的鍵可被用來限定特定條件的表格名稱。例如
Student.joins(:schools).where(schools: { category: 'public' })
Student.joins(:schools).where('schools.category' => 'public' )
覆寫預設存取器
所有欄位值都可透過 Active Record 物件上的基本存取器自動取得,但有時您會想要特別化此行為。這可透過覆寫預設存取器(使用與屬性相同的名稱)並呼叫 super
來實際變更內容。
class Song < ActiveRecord::Base
# Uses an integer of seconds to hold the length of the song
def length=(minutes)
super(minutes.to_i * 60)
end
def length
super / 60
end
end
屬性查詢方法
除了基本存取器之外,查詢方法也可透過 Active Record 物件自動取得。查詢方法讓您可以測試屬性值是否存在。此外,在處理數值時,如果值為零,查詢方法會傳回 false。
例如,具有 name
屬性的 Active Record User 有 name?
方法,您可以呼叫它來判斷使用者是否有姓名
user = User.new(name: "David")
user.name? # => true
anonymous = User.new(name: "")
anonymous.name? # => false
查詢方法也會尊重預設存取器的任何覆寫
class User
# Has admin boolean column
def admin
false
end
end
user.update(admin: true)
user.read_attribute(:admin) # => true, gets the column value
user[:admin] # => true, also gets the column value
user.admin # => false, due to the getter override
user.admin? # => false, due to the getter override
在類型轉換之前存取屬性
有時您會想要能夠在欄位決定的類型轉換執行之前讀取原始屬性資料。這可透過使用所有屬性都具有的 <attribute>_before_type_cast
存取器來完成。例如,如果您的 Account 模型有 balance
屬性,您可以呼叫 account.balance_before_type_cast
或 account.id_before_type_cast
。
這在驗證情況中特別有用,在這種情況下,使用者可能會為整數欄位提供字串,而您想要在錯誤訊息中顯示原始字串。正常存取屬性會將字串類型轉換為 0,這不是您想要的。
動態屬性為基礎的尋找器
動態屬性為基礎的尋找器是一種稍微不建議使用的方式,可透過簡單的查詢取得(和/或建立)物件,而無需轉換為 SQL。它們透過將屬性名稱附加到 find_by_
來運作,例如 Person.find_by_user_name
。您可使用 Person.find_by_user_name(user_name)
,而不是撰寫 Person.find_by(user_name: user_name)
。
可以在動態尋找器的尾端加上驚嘆號 (!),讓它們在沒有傳回任何記錄時引發 ActiveRecord::RecordNotFound
錯誤,例如 Person.find_by_last_name!
。
也可以在同一個 find_by_
中使用多個屬性,方法是使用「and」將它們分隔開來。
Person.find_by(user_name: user_name, password: password)
Person.find_by_user_name_and_password(user_name, password) # with dynamic finder
甚至可以在關聯和命名範圍上呼叫這些動態尋找器方法。
Payment.order("created_on").find_by_amount(50)
在文字欄位中儲存陣列、雜湊和其他不可對應的物件
Active Record 可以使用 YAML 在文字欄位中序列化任何物件。為此,您必須使用對類別方法 serialize 的呼叫來指定這一點。這使得儲存陣列、雜湊和其他不可對應的物件成為可能,而無需執行任何額外的作業。
class User < ActiveRecord::Base
serialize :preferences
end
user = User.create(preferences: { "background" => "black", "display" => large })
User.find(user.id).preferences # => { "background" => "black", "display" => large }
您還可以將類別選項指定為第二個參數,如果將序列化的物件檢索為不屬於層級結構中類別的後代,則該參數會引發例外狀況。
class User < ActiveRecord::Base
serialize :preferences, Hash
end
user = User.create(preferences: %w( one two three ))
User.find(user.id).preferences # raises SerializationTypeMismatch
當您指定類別選項時,該屬性的預設值將是該類別的新執行個體。
class User < ActiveRecord::Base
serialize :preferences, OpenStruct
end
user = User.new
user.preferences.theme_color = "red"
單一表格繼承
Active Record 允許繼承,方法是在預設名稱為「類型」的欄位中儲存類別名稱。有關更多詳細資訊,請參閱 ActiveRecord::Inheritance
。
在不同模型中連線到多個資料庫
連線通常透過 ActiveRecord::Base.establish_connection 建立,並透過 ActiveRecord::Base.connection 檢索。所有繼承自 ActiveRecord::Base
的類別都將使用此連線。但您也可以設定類別特定的連線。例如,如果 Course 是 ActiveRecord::Base
,但存在於不同的資料庫中,您只要說 Course.establish_connection
,Course 及其所有子類別就會使用此連線。
此功能是透過在 ActiveRecord::Base
中保留連線池來實作的,連線池是一個以類別為索引的雜湊。如果要求連線,ActiveRecord::Base.retrieve_connection 方法將沿著類別層級向上,直到在連線池中找到連線為止。
例外狀況
-
ActiveRecordError
- 一般錯誤類別,也是 Active Record 引發的所有其他錯誤的超類別。 -
AdapterNotSpecified
- 在 ActiveRecord::Base.establish_connection 中使用的組態雜湊不包含:adapter
鍵。 -
AdapterNotFound
- 在 ActiveRecord::Base.establish_connection 中使用的:adapter
鍵指定了不存在的介面卡 (或拼寫錯誤的介面卡)。 -
AssociationTypeMismatch
- 指定給關聯的物件不是關聯定義中指定的類型。 -
AttributeAssignmentError
- 透過 ActiveRecord::Base#attributes= 方法進行大量指定時發生錯誤。您可以檢查例外狀況物件的attribute
屬性,以確定觸發錯誤的屬性。 -
ConnectionNotEstablished
- 尚未建立連線。在查詢之前,請使用 ActiveRecord::Base.establish_connection。 -
MultiparameterAssignmentErrors
- 使用 ActiveRecord::Base#attributes= 方法進行大量指定期間發生的錯誤集合。此例外狀況的errors
屬性包含AttributeAssignmentError
物件陣列,應檢查該陣列以確定觸發錯誤的屬性。 -
RecordInvalid
- 當記錄無效時,由 ActiveRecord::Base#save! 和 ActiveRecord::Base.create! 引發。 -
RecordNotFound
- 沒有記錄回應 ActiveRecord::Base.find 方法。具有給定 ID 的列不存在,或列不符合其他限制。有些 ActiveRecord::Base.find 呼叫不會引發此例外情況來表示未找到任何內容,請查看其文件以取得進一步的詳細資料。 -
SerializationTypeMismatch
- 序列化物件不是指定為第二個參數的類別。 -
StatementInvalid
- 資料庫伺服器拒絕 SQL 陳述式。訊息中會新增精確的錯誤。
注意:所列的屬性是類別層級屬性(可從類別和實例層級存取)。因此,可以透過 Base.logger=
將記錄器指定給類別,然後由目前物件空間中的所有實例使用。
- ActiveRecord::Core
- ActiveRecord::Persistence
- ActiveRecord::ReadonlyAttributes
- ActiveRecord::ModelSchema
- ActiveRecord::Inheritance
- ActiveRecord::Scoping
- ActiveRecord::Sanitization
- ActiveRecord::AttributeAssignment
- ActiveModel::Conversion
- ActiveRecord::Integration
- ActiveRecord::Validations
- ActiveRecord::CounterCache
- ActiveRecord::Attributes
- ActiveRecord::Locking::Optimistic
- ActiveRecord::Locking::Pessimistic
- ActiveRecord::Encryption::EncryptableRecord
- ActiveRecord::AttributeMethods
- ActiveRecord::Callbacks
- ActiveRecord::Timestamp
- ActiveRecord::Associations
- ActiveRecord::SecurePassword
- ActiveRecord::AutosaveAssociation
- ActiveRecord::NestedAttributes
- ActiveRecord::Transactions
- ActiveRecord::NoTouching
- ActiveRecord::Reflection
- ActiveRecord::AttributeMethods::Serialization
- ActiveRecord::Store
- ActiveRecord::SecureToken
- ActiveRecord::TokenFor
- ActiveRecord::SignedId
- ActiveRecord::Suppressor
- ActiveRecord::Normalization
- ActiveRecord::Marshalling::Methods