跳至内容 跳至搜索

Active Record – Ruby on Rails 中的对象关系映射

Active Record 将类连接到关系型数据库表,为应用程序建立几乎零配置的持久层。该库提供了一个基类,当被继承时,它会在新类和数据库中现有的表之间建立映射。在应用程序中,这些类通常被称为 **模型**。模型也可以与其他模型关联;这是通过定义 **关联** 来实现的。

Active Record 严重依赖命名,因为它使用类名和关联名来建立相应数据库表和外键列之间的映射。尽管可以显式定义这些映射,但建议遵循命名约定,尤其是在开始使用该库时。

您可以在 Active Record 基础知识 指南中阅读更多关于 Active Record 的信息。

一些主要功能的简要介绍

  • 类和表、属性和列之间的自动映射。

    class Product < ActiveRecord::Base
    end
    

    Product 类会自动映射到名为“products”的表,该表可能如下所示:

    CREATE TABLE products (
      id bigint NOT NULL auto_increment,
      name varchar(255),
      PRIMARY KEY  (id)
    );

    这还会定义以下访问器:Product#nameProduct#name=(new_name)

    了解更多

  • 关联 通过简单的类方法在对象之间建立。

    class Firm < ActiveRecord::Base
      has_many   :clients
      has_one    :account
      belongs_to :conglomerate
    end
    

    了解更多

  • 聚合 值对象。

    class Account < ActiveRecord::Base
      composed_of :balance, class_name: 'Money',
                  mapping: %w(balance amount)
      composed_of :address,
                  mapping: [%w(address_street street), %w(address_city city)]
    end
    

    了解更多

  • 验证规则,对于新对象或现有对象可以不同。

    class Account < ActiveRecord::Base
      validates :subdomain, :name, :email_address, :password, presence: true
      validates :subdomain, uniqueness: true
      validates :terms_of_service, acceptance: true, on: :create
      validates :password, :email_address, confirmation: true, on: :create
    end
    

    了解更多

  • 在整个生命周期(实例化、保存、销毁、验证等)中都可以使用的 回调

    class Person < ActiveRecord::Base
      before_destroy :invalidate_payment_plan
      # the `invalidate_payment_plan` method gets called just before Person#destroy
    end
    

    了解更多

  • 继承 层级。

    class Company < ActiveRecord::Base; end
    class Firm < Company; end
    class Client < Company; end
    class PriorityClient < Client; end
    

    了解更多

  • 事务.

    # Database transaction
    Account.transaction do
      david.withdrawal(100)
      mary.deposit(100)
    end
    

    了解更多

  • 关于列、关联和聚合的反射。

    reflection = Firm.reflect_on_association(:clients)
    reflection.klass # => Client (class)
    Firm.columns # Returns an array of column descriptors for the firms table
    

    了解更多

  • 通过简单的适配器实现数据库抽象。

    # connect to SQLite3
    ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: 'dbfile.sqlite3')
    
    # connect to MySQL with authentication
    ActiveRecord::Base.establish_connection(
      adapter:  'mysql2',
      host:     'localhost',
      username: 'me',
      password: 'secret',
      database: 'activerecord'
    )
    

    了解更多 并阅读内置对 MySQLPostgreSQLSQLite3 的支持。

  • Log4rLogger 的日志支持。

    ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
    ActiveRecord::Base.logger = Log4r::Logger.new('Application Log')
    
  • 通过 Migrations 实现数据库无关的模式管理。

    class AddSystemSettings < ActiveRecord::Migration[8.1]
      def up
        create_table :system_settings do |t|
          t.string  :name
          t.string  :label
          t.text    :value
          t.string  :type
          t.integer :position
        end
    
        SystemSetting.create name: 'notice', label: 'Use notice?', value: 1
      end
    
      def down
        drop_table :system_settings
      end
    end
    

    了解更多

理念

Active Record 是 Martin Fowler 所描述的同名 模式 的对象关系映射(ORM)实现。

“一个包装数据库表或视图中的一行,封装数据库访问,并在该数据上添加域逻辑的对象。”

Active Record 试图提供一个连贯的包装器,以解决对象关系映射带来的不便。该映射的首要指令是最大程度地减少构建真实域模型所需的代码量。通过依赖一系列约定,Active Record 可以轻松地从最少的显式指示中推断出复杂的关联和结构。

约定优于配置

  • 无 XML 文件!

  • 大量反射和运行时扩展

  • 魔术(Magic)本身不是一个坏词

承认数据库

  • 允许您在特殊情况和性能方面直接使用 SQL。

  • 不试图复制或替换数据定义

下载和安装

最新版本的 Active Record 可以通过 RubyGems 安装。

$ gem install activerecord

源代码可以作为 Rails 项目的一部分在 GitHub 上下载。

许可证

Active Record 在 MIT 许可证下发布。

支持

API 文档位于:

Ruby on Rails 项目的错误报告可在此处提交:

功能请求应在此处的 rubyonrails-core 论坛上讨论:

用于包装关联记录错误的验证错误类,支持 index_errors。

命名空间
方法
A
D
E
G
L
M
P
Q
R
S
T
U
V
W
Y
包含的模块

常量

MigrationProxy = Struct.new(:name, :version, :filename, :scope) do def initialize(name, version, filename, scope) super @migration = nil end def basename File.basename(filename) end delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration private def migration @migration ||= load_migration end def load_migration Object.send(:remove_const, name) rescue nil load(File.expand_path(filename)) name.constantize.new(name, version) end end
 

MigrationProxy 用于延迟加载实际的迁移类,直到需要它们为止。

Point = Struct.new(:x, :y)
 
UnknownAttributeError = ActiveModel::UnknownAttributeError
 

Active Model UnknownAttributeError

在通过质量赋值提供未知属性时引发。

class Person
  include ActiveModel::AttributeAssignment
  include ActiveModel::Validations
end

person = Person.new
person.assign_attributes(name: 'Gorby')
# => ActiveModel::UnknownAttributeError: unknown attribute 'name' for Person.

Attributes

[RW] application_record_class
[RW] before_committed_on_all_records
[RW] belongs_to_required_validates_foreign_key
[RW] database_cli
[R] default_timezone
[RW] disable_prepared_statements
[RW] index_nested_attribute_errors
[RW] maintain_test_schema
[R] permanent_connection_checkout
[RW] query_transformers
[RW] raise_on_assign_to_attr_readonly
[RW] raise_on_missing_required_finder_order_columns
[RW] reading_role
[RW] run_after_transaction_callbacks_in_order_defined
[RW] writing_role

类公共方法

action_on_strict_loading_violation

设置应用程序在关联违反严格加载时记录或引发异常。默认为 `:raise`。

# File activerecord/lib/active_record.rb, line 370
singleton_class.attr_accessor :action_on_strict_loading_violation

after_all_transactions_commit(&block)

注册一个块,在所有当前事务提交后调用。

如果没有当前打开的事务,块将立即调用。

如果有多个嵌套事务,块将在最外层事务提交后调用。

如果任何当前打开的事务被回滚,块将永远不会被调用。

如果有多个事务在多个数据库上打开,当所有事务都提交后,该块将被调用。但请注意,跨两个不同数据库的嵌套事务是分片反模式,会带来很多问题。

# File activerecord/lib/active_record.rb, line 573
def self.after_all_transactions_commit(&block)
  open_transactions = all_open_transactions

  if open_transactions.empty?
    yield
  elsif open_transactions.size == 1
    open_transactions.first.after_commit(&block)
  else
    count = open_transactions.size
    callback = -> do
      count -= 1
      block.call if count.zero?
    end
    open_transactions.each do |t|
      t.after_commit(&callback)
    end
    open_transactions = nil # rubocop:disable Lint/UselessAssignment avoid holding it in the closure
  end
end

async_query_executor

为应用程序设置 `async_query_executor`。默认情况下,线程池执行程序设置为 `nil`,这不会在后台运行查询。应用程序必须配置一个线程池执行程序才能使用此功能。选项有:

* nil - Does not initialize a thread pool executor. Any async calls will be
run in the foreground.
* :global_thread_pool - Initializes a single +Concurrent::ThreadPoolExecutor+
that uses the +async_query_concurrency+ for the +max_threads+ value.
* :multi_thread_pool - Initializes a +Concurrent::ThreadPoolExecutor+ for each
database connection. The initializer values are defined in the configuration hash.
# File activerecord/lib/active_record.rb, line 288
singleton_class.attr_accessor :async_query_executor

db_warnings_action

当数据库查询产生警告时要采取的操作。必须是 `:ignore`、`:log`、`:raise`、`:report` 或自定义 proc 之一。默认为 `:ignore`。

# File activerecord/lib/active_record.rb, line 235
singleton_class.attr_reader :db_warnings_action

db_warnings_action=(action)

# File activerecord/lib/active_record.rb, line 237
def self.db_warnings_action=(action)
  @db_warnings_action =
    case action
    when :ignore
      nil
    when :log
      ->(warning) do
        warning_message = "[#{warning.class}] #{warning.message}"
        warning_message += " (#{warning.code})" if warning.code
        ActiveRecord::Base.logger.warn(warning_message)
      end
    when :raise
      ->(warning) { raise warning }
    when :report
      ->(warning) { Rails.error.report(warning, handled: true) }
    when Proc
      action
    else
      raise ArgumentError, "db_warnings_action must be one of :ignore, :log, :raise, :report, or a custom proc."
    end
end

db_warnings_ignore

指定数据库警告的允许列表。可以是字符串、正则表达式或数据库的错误代码。

ActiveRecord::Base.db_warnings_ignore = [/`SHOW WARNINGS` did not return the warnings/, "01000"]
# File activerecord/lib/active_record.rb, line 267
singleton_class.attr_accessor :db_warnings_ignore

default_timezone=(default_timezone)

确定是从数据库中读取日期和时间时使用 `Time.utc`(使用 `:utc`)还是 `Time.local`(使用 `:local`)。默认为 `:utc`。

# File activerecord/lib/active_record.rb, line 220
def self.default_timezone=(default_timezone)
  unless %i(local utc).include?(default_timezone)
    raise ArgumentError, "default_timezone must be either :utc (default) or :local."
  end

  @default_timezone = default_timezone
end

deprecated_associations_options()

# File activerecord/lib/active_record.rb, line 495
def self.deprecated_associations_options
  {
    mode: ActiveRecord::Associations::Deprecation.mode,
    backtrace: ActiveRecord::Associations::Deprecation.backtrace
  }
end

deprecated_associations_options=(options)

# File activerecord/lib/active_record.rb, line 479
def self.deprecated_associations_options=(options)
  raise ArgumentError, "deprecated_associations_options must be a hash" unless options.is_a?(Hash)

  valid_keys = [:mode, :backtrace]

  invalid_keys = options.keys - valid_keys
  unless invalid_keys.empty?
    inflected_key = invalid_keys.size == 1 ? "key" : "keys"
    raise ArgumentError, "invalid deprecated_associations_options #{inflected_key} #{invalid_keys.map(&:inspect).to_sentence} (valid keys are #{valid_keys.map(&:inspect).to_sentence})"
  end

  options.each do |key, value|
    ActiveRecord::Associations::Deprecation.send("#{key}=", value)
  end
end

disconnect_all!()

显式关闭所有连接池中的所有数据库连接。

# File activerecord/lib/active_record.rb, line 556
def self.disconnect_all!
  ConnectionAdapters::PoolConfig.disconnect_all!
end

dump_schema_after_migration

指定 `bin/rails db:migrate` 命令结束时是否应进行模式转储。默认情况下此值为 `true`,这在开发环境中很有用。在生产环境中,此值最好为 `false`,因为在生产环境中很少需要转储模式。

# File activerecord/lib/active_record.rb, line 425
singleton_class.attr_accessor :dump_schema_after_migration

dump_schemas

指定调用 `db:schema:dump` 时要转储的数据库模式。如果值为 `:schema_search_path`(默认值),则转储 `schema_search_path` 中列出的任何模式。使用 `:all` 来转储所有模式(无论 `schema_search_path` 如何),或使用逗号分隔的字符串来指定自定义列表。

# File activerecord/lib/active_record.rb, line 435
singleton_class.attr_accessor :dump_schemas

eager_load!()

# File activerecord/lib/active_record.rb, line 545
def self.eager_load!
  super
  ActiveRecord::Locking.eager_load!
  ActiveRecord::Scoping.eager_load!
  ActiveRecord::Associations.eager_load!
  ActiveRecord::AttributeMethods.eager_load!
  ActiveRecord::ConnectionAdapters.eager_load!
  ActiveRecord::Encryption.eager_load!
end

error_on_ignored_order

指定在批处理查询中忽略 `order` 时是否应引发错误。当应用程序中的作用域被视为错误而不是警告时很有用。

# File activerecord/lib/active_record.rb, line 390
singleton_class.attr_accessor :error_on_ignored_order

gem_version()

将当前加载的 Active Record 版本作为 `Gem::Version` 返回。

# File activerecord/lib/active_record/gem_version.rb, line 5
def self.gem_version
  Gem::Version.new VERSION::STRING
end

generate_secure_token_on

控制何时为 `has_secure_token` 声明生成值。默认为 `:create`。

# File activerecord/lib/active_record.rb, line 476
singleton_class.attr_accessor :generate_secure_token_on

global_executor_concurrency=(global_executor_concurrency)

设置 `global_executor_concurrency`。此配置值只能与全局线程池异步查询执行程序一起使用。

# File activerecord/lib/active_record.rb, line 304
def self.global_executor_concurrency=(global_executor_concurrency)
  if self.async_query_executor.nil? || self.async_query_executor == :multi_thread_pool
    raise ArgumentError, "`global_executor_concurrency` cannot be set when the executor is nil or set to `:multi_thread_pool`. For multiple thread pools, please set the concurrency in your database configuration."
  end

  @global_executor_concurrency = global_executor_concurrency
end

lazily_load_schema_cache

延迟加载模式缓存。此选项将在建立连接时加载模式缓存,而不是在启动时加载。

# File activerecord/lib/active_record.rb, line 191
singleton_class.attr_accessor :lazily_load_schema_cache

marshalling_format_version()

# File activerecord/lib/active_record.rb, line 502
def self.marshalling_format_version
  Marshalling.format_version
end

marshalling_format_version=(value)

# File activerecord/lib/active_record.rb, line 506
def self.marshalling_format_version=(value)
  Marshalling.format_version = value
end

message_verifiers

`ActiveSupport::MessageVerifiers` 实例用于 Active Record。如果您正在使用 `Rails`,它将被设置为 `Rails.application.message_verifiers`。

# File activerecord/lib/active_record.rb, line 543
singleton_class.attr_accessor :message_verifiers

migration_strategy

指定用于执行迁移的策略。

# File activerecord/lib/active_record.rb, line 410
singleton_class.attr_accessor :migration_strategy

permanent_connection_checkout=(value)

定义 `ActiveRecord::Base.connection` 是允许、已弃用还是完全禁止。

# File activerecord/lib/active_record.rb, line 320
def self.permanent_connection_checkout=(value)
  unless [true, :deprecated, :disallowed].include?(value)
    raise ArgumentError, "permanent_connection_checkout must be one of: `true`, `:deprecated` or `:disallowed`"
  end
  @permanent_connection_checkout = value
end

protocol_adapters

在数据库协议/DBMS 和要使用的底层数据库适配器之间提供映射。这仅由 `DATABASE_URL` 环境变量使用。

示例

DATABASE_URL="mysql://myuser:mypass@localhost/somedatabase"

上述 URL 指定 MySQL 是所需的协议/DBMS,然后应用程序配置可以选择使用哪个适配器。在此示例中,默认映射是从 `mysql` 到 `mysql2`,但也支持 `:trilogy`。

ActiveRecord.protocol_adapters.mysql = "mysql2"

协议名称是任意的,并且可以在此处注册和设置外部数据库适配器。

# File activerecord/lib/active_record.rb, line 529
singleton_class.attr_accessor :protocol_adapters

queues

指定后台作业使用的队列名称。

# File activerecord/lib/active_record.rb, line 342
singleton_class.attr_accessor :queues

raise_int_wider_than_64bit

应用程序可配置的布尔值,表示 PostgreSQLAdapter 是否在提供大于 64 位有符号整数的值时引发异常。

# File activerecord/lib/active_record.rb, line 462
singleton_class.attr_accessor :raise_int_wider_than_64bit

schema_cache_ignored_table?(table_name)

通过检查 `schema_cache_ignored_tables` 选项来判断 `table_name` 是否被忽略。

ActiveRecord.schema_cache_ignored_table?(:developers)
# File activerecord/lib/active_record.rb, line 207
def self.schema_cache_ignored_table?(table_name)
  ActiveRecord.schema_cache_ignored_tables.any? do |ignored|
    ignored === table_name
  end
end

schema_cache_ignored_tables

在转储模式缓存时要忽略的表或匹配表的正则表达式列表。例如,如果此选项设置为 `[/^_/]`,则模式缓存将不会转储以下划线开头的表。

# File activerecord/lib/active_record.rb, line 199
singleton_class.attr_accessor :schema_cache_ignored_tables

schema_format

指定使用 Rails 的 Rakefile 转储数据库模式时使用的格式。如果为 `:sql`,则模式将作为(可能是特定于数据库的)SQL 语句转储。如果为 `:ruby`,则模式将作为可以加载到任何支持迁移的数据库中的 `ActiveRecord::Schema` 文件转储。如果您希望为您的开发和测试环境使用不同的数据库适配器,请使用 `:ruby`。这可以在数据库配置中为每个数据库单独覆盖。

# File activerecord/lib/active_record.rb, line 382
singleton_class.attr_accessor :schema_format

schema_versions_formatter

指定模式转储程序用于格式化版本信息的分隔符。

# File activerecord/lib/active_record.rb, line 416
singleton_class.attr_accessor :schema_versions_formatter

timestamped_migrations

指定是否使用时间戳作为迁移版本。

# File activerecord/lib/active_record.rb, line 396
singleton_class.attr_accessor :timestamped_migrations

use_yaml_unsafe_load

应用程序可配置的布尔值,指示 YAML 编码器在设置为 `true` 时使用不安全的加载。

# File activerecord/lib/active_record.rb, line 454
singleton_class.attr_accessor :use_yaml_unsafe_load

validate_migration_timestamps

指定是否验证迁移时间戳。设置为 `true` 时,如果时间戳比当前时间相关时间戳早一天以上,将引发错误。必须将 `timestamped_migrations` 设置为 `true`。

# File activerecord/lib/active_record.rb, line 404
singleton_class.attr_accessor :validate_migration_timestamps

verbose_query_logs

指定是否应在数据库查询旁边记录调用数据库查询的方法。默认为 `false`。

# File activerecord/lib/active_record.rb, line 335
singleton_class.attr_accessor :verbose_query_logs

verify_foreign_keys_for_fixtures

如果为 `true`,`Rails` 将在加载 fixture 后验证数据库中的所有外键。如果存在任何外键冲突,将引发错误,表明 fixture 编写不正确。支持 PostgreSQL 和 SQLite。

# File activerecord/lib/active_record.rb, line 444
singleton_class.attr_accessor :verify_foreign_keys_for_fixtures

version()

将当前加载的 Active Record 版本作为 `Gem::Version` 返回。

# File activerecord/lib/active_record/version.rb, line 7
def self.version
  gem_version
end

with_transaction_isolation_level(isolation_level, &block)

在块中的所有连接池中设置事务隔离级别。

# File activerecord/lib/active_record.rb, line 616
def self.with_transaction_isolation_level(isolation_level, &block)
  original_level = self.default_transaction_isolation_level
  self.default_transaction_isolation_level = isolation_level
  yield
ensure
  self.default_transaction_isolation_level = original_level
end

yaml_column_permitted_classes

应用程序可配置的数组,为 YAML 编码器中的 Psych safe_load 提供额外的允许类。

# File activerecord/lib/active_record.rb, line 469
singleton_class.attr_accessor :yaml_column_permitted_classes