跳至内容 跳至搜索

Active Record – 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')
    
  • 使用迁移进行数据库无关的模式管理。

    class AddSystemSettings < ActiveRecord::Migration[8.0]
      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 文件!

  • 大量的反射和运行时扩展

  • 魔法本身并不是一个坏词

承认数据库

  • 允许您在特殊情况下和性能方面降级到 SQL

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

下载和安装

可以使用 RubyGems 安装最新版本的 Active Record

$ gem install activerecord

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

许可证

Active Record 在 MIT 许可下发布

支持

API 文档位于

Ruby on Rails 项目的错误报告可以在这里提交

功能请求应该在以下位置的 rails-core 邮件列表中讨论

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

命名空间
方法
A
D
E
G
L
M
P
Q
R
S
T
U
V
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 用于延迟加载实际的迁移类,直到它们被需要。

= 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.

属性

[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] 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 361
singleton_class.attr_accessor :action_on_strict_loading_violation

after_all_transactions_commit(&block)

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

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

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

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

如果在多个数据库中打开了多个事务,则在所有事务提交后调用该块。但是请注意,跨两个不同数据库嵌套事务是一种分片反模式,会带来许多问题。

# File activerecord/lib/active_record.rb, line 527
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 283
singleton_class.attr_accessor :async_query_executor

db_warnings_action

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

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

db_warnings_action=(action)

# File activerecord/lib/active_record.rb, line 235
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

指定数据库警告的白名单。

# File activerecord/lib/active_record.rb, line 262
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 218
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

disconnect_all!()

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

# File activerecord/lib/active_record.rb, line 510
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 409
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 419
singleton_class.attr_accessor :dump_schemas

eager_load!()

# File activerecord/lib/active_record.rb, line 499
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

指定在执行批量查询时,如果查询具有被忽略的排序,是否应该引发错误。在应用中,被忽略的范围是错误,而不是警告,在这种情况下很有用。

# File activerecord/lib/active_record.rb, line 380
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 460
singleton_class.attr_accessor :generate_secure_token_on

global_executor_concurrency=(global_executor_concurrency)

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

# File activerecord/lib/active_record.rb, line 298
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 189
singleton_class.attr_accessor :lazily_load_schema_cache

marshalling_format_version()

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

marshalling_format_version=(value)

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

migration_strategy

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

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

permanent_connection_checkout=(value)

定义是否允许、弃用或完全禁止 ActiveRecord::Base.connection

# File activerecord/lib/active_record.rb, line 314
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,然后应用程序配置可以决定使用哪个适配器。对于此示例,默认映射是从 mysqlmysql2,但也支持 :trilogy

ActiveRecord.protocol_adapters.mysql = "mysql2"

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

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

queues

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

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

raise_int_wider_than_64bit

应用程序可配置的布尔值,表示当 PostgreSQLAdapter 提供比带符号 64 位表示更宽的整数时,是否引发异常。

# File activerecord/lib/active_record.rb, line 446
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 205
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 197
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 372
singleton_class.attr_accessor :schema_format

timestamped_migrations

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

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

use_yaml_unsafe_load

应用程序可配置的布尔值,指示 YAML Coder 如果设置为 true,则使用不安全的加载。

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

validate_migration_timestamps

指定是否验证迁移时间戳。如果设置,则如果时间戳比与当前时间相关联的时间戳提前一天以上,则会引发错误。timestamped_migrations 必须设置为 true。

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

verbose_query_logs

指定是否在相关查询下方记录调用数据库查询的方法。默认值为 false。

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

verify_foreign_keys_for_fixtures

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

# File activerecord/lib/active_record.rb, line 428
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

yaml_column_permitted_classes

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

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