ActiveRecord
ActiveRecord 对象不直接指定其属性,而是从与其链接的表定义中推断出其属性。在数据库中直接添加、删除和更改属性及其类型。任何更改都会立即反映在 ActiveRecord 对象中。在大多数情况下,将给定的 ActiveRecord 类绑定到某个数据库表的映射会自动发生,但可以为不常见的情况覆盖映射。
请参阅 table_name 中的映射规则以及 files/activerecord/README_rdoc.html 中的完整示例以了解更多见解。
创建
Active Records 接受哈希或块形式的构造函数参数。当您从其他地方(如 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!
。
还可以通过用“and”分隔多个属性在同一个 find_by_
中使用多个属性。
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 允许通过将类的名称存储在默认情况下名为“type”的列中来实现继承。有关更多详细信息,请参阅 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