Active Record 中的 10 个新特性
最初发布于Hint 的博客。
在这篇文章中,我们将介绍 Rails 6 中 Active Record 的 10 个新增功能。对于每个新增功能,我都会提供该功能所在的 PR 链接、作者的 GitHub 个人资料链接以及该功能提供的简要说明。
我们有很多内容要讲,那就开始吧!
1.rails db:prepare
PR 35678,作者:@robertomiranda
运行此 rake 任务时,如果数据库存在,则运行所有待处理的迁移。如果数据库不存在,则运行db:setuprake 任务本身。
此功能设计为幂等的,允许反复运行直到成功完成。
该rake db:prepare任务的功能如下:
如果你还不熟悉db:setuprake 任务:
- 创建数据库
- 加载文件
schema.rb(structure.sql取决于您的应用程序配置使用的文件)。 - 运行该任务,该任务会执行文件
db:seed中的代码。db/seeds.rb
2.rails db:seed:replant
PR 34779,作者:@bogdanvlviv
这个新的db:seed:replantrake 任务做了两件事:
truncate会对运行 rake 任务的 Rails 环境中所有由 ActiveRecord 管理的表进行操作。(请注意,此truncate操作会删除表中的所有数据,但不会重置表的自增(ID)计数器。)- 运行
db:seedRails rake 任务以填充种子数据
3. 自动数据库切换
PR 35073,作者:@eileencodes
Rails 6 提供了一个框架,可以将传入的请求自动路由到主数据库连接或只读副本。
默认情况下,如果距离上次写入请求(任何非写入或写入请求)至少 2 秒,则此新功能允许您的应用程序自动将读取请求(GET, )路由到读取 Relica 数据库。HEADGETHEAD
默认情况下,用于指定何时将读取请求路由到副本的逻辑是在解析器类中指定的,ActiveRecord::Middleware::DatabaseSelector::Resolver如果您想要自定义行为,则可以覆盖该解析器类。
中间件还提供了一个会话类,ActiveRecord::Middleware::DatabaseSelector::Resolver::Session用于跟踪上次写入请求的时间。与解析器一样,该类也可以被重写。
要启用默认行为,您需要将以下配置选项添加到应用程序的某个环境文件中,config/environments/production.rb例如:
config.active_record.database_selector = { delay: 2.seconds }
config.active_record.database_resolver =
ActiveRecord::Middleware::DatabaseSelector::Resolver
config.active_record.database_operations =
ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
如果您决定覆盖默认功能,可以使用这些配置选项来指定您想要使用的延迟、自定义解析器类的名称和自定义会话类的名称,这两个类都应该是默认类的子类。
4. 枚举的否定作用域
PR 35381由@dhh提供
虽然enum传统上提供了按枚举值查找项的作用域,但它没有提供查找与特定枚举值不匹配的项的作用域。
例如,给定Post一个博客模型,其字段具有枚举类型status:
enum status %i(draft published archived)
以下范围已自动提供一段时间:
scope :draft, -> { where(status: 0) }
scope :published, -> { where(status: 1) }
scope :archived, -> { where(status: 2) }
现在,还可以使用以下负片示波器:
scope :not_draft, -> { where.not(status: 0) }
scope :not_published, -> { where.not(status: 1) }
scope :not_archived, -> { where.not(status: 2) }
例如,这使得查找未发布的文章变得很容易:
Post.not_published
5.#extract_associated
PR 35784,作者:@dhh
新方法实际上只是加号#extract_associated的简写。preloadmap/collect
以下是该方法的源代码:
def extract_associated(association)
preload(association).collect(&association)
end
请注意,它preload不允许您指定关联“预加载”的条件。您需要使用不同的预加载机制,例如 `get_extainer_reloaded`或 ` includesget_extainer_reloaded` 。eager_loadjoins
用法#extract_associated可能如下所示:
commented_posts = user.comments.extract_associated(:post)
6.#annotate
PR 35617,作者:@mattyoho
这是一个非常实用的新功能,可用于向应用程序的日志文件中添加有用的信息。该#annotate方法提供了一种机制,可以将注释嵌入到 ActiveRecord 查询生成的 SQL 语句中。此外,它生成的注释还可以完全动态化。
像下面这样插入annotate到查询链中:
User
.annotate('there can be only one!')
.find_by(highlander: true)
将生成以下 SQL 语句:
SELECT "users".*
FROM "users"
WHERE "users"."highlander" = ? /* there can be only one! */
LIMIT ? [["highlander", 1], ["LIMIT", 1]]
7.#touch_all
PR 31513,作者:@fatkodima
另一种ActiveRecord::Relation方法是#touch_all修改当前范围内的所有记录,更新它们的时间戳。
你可以向 touch 传递一个列数组,并可选择提供要使用的时间值。touch_all默认值为应用程序配置中设置的时区的当前时间config.active_record.default_timezone(该设置默认为 UTC)。
例如,要更新updated_at与特定博客文章关联的所有评论字段@post,您可以:
@post.comments.touch_all
要更新评论中的某个字段,例如:reviewed_at,您需要提供列名:
@post.comments.touch_all(:reviewed_at)
要指定时间值,您需要:
@post.comments.touch_all(:reviewed_at, time: the_time)
8.#destroy_by和#delete_by
PR 35316,作者:@abhaynikam
这些destroy_by方法delete_by旨在与's和方法提供对称性( “精神上” )。ActiveRecordfind_byfind_or_create_by
我认为你应该注意一个重要的区别。`returns find_byone record` 或 `returns` nil,而destroy_by`and`delete_by将匹配整个记录集合。
使用方法find_by如下:
User.find_by(admin: true)
生成以下 SQL 语句:
SELECT "users".*
FROM "users"
WHERE "users"."admin" = $1
LIMIT 1 [["admin", 1]]
然而,使用delete_by相同的参数:
User.delete_by(admin: true)
结果生成以下 SQL 语句:
DELETE FROM "users"
WHERE "users"."admin" = ? [["admin", 1]]
使用这些方法时,一定要牢记这一点!
另外,值得注意的是,这些方法没有带感叹号(!)的版本delete_by/destroy_by。
9. 无尽的范围#where
PR 34906,作者:@gregnavis
Ruby 2.6 引入了无限范围。这项新特性允许你在 Rails 的#where条件语句中使用它们。
例如,当尝试查找评论数超过 10 条的帖子时(我知道这是一个牵强的例子)。
以前你需要使用类似这样的 SQL:
Post.where('num_comments > ?', 10)
现在你可以使用更符合 Ruby 惯用的语法了:
User.where(num_comments: (10..))
10. 隐式排序
PR 34480,作者:@tekin
此功能允许为数据库表配置隐式排序。Rails 默认按表的主键对结果进行隐式排序,但当主键不是自增整数(例如 UUID)时,可能会产生意想不到的结果。
设置表的隐式排序列允许您指定默认顺序,而无需使用默认范围,这意味着您无需reorder在代码中使用来更改下游的顺序,您可以使用order(假设您尚未在查询链的前面指定显式排序)。
例如,如果您在Post表中声明了隐式排序:
class Post < ActiveRecord::Base
self.implicit_order_column = 'title'
end
请注意,如果您对不能保证唯一值的列声明隐式排序,则结果可能与您预期的不同。
文章来源:https://dev.to/hint/10-new-things-in-active-record-5112
