rails 指南总结(六)——Model之Active Record 查询接口

1从数据库中检索对象

1.1 检索单个对象

1.1.1 find

  • .client = Client.find(10)
    使用 find 方法检索指定主键对应的对象,指定主键时可以使用多个选项,等同于:
SELECT * FROM clients WHERE (clients.id = 10) LIMIT 1
  • client = Client.find([1, 10]),等同于:
SELECT * FROM clients WHERE (clients.id IN (1,10))

如果没有找到匹配的记录,find 方法抛出 ActiveRecord::RecordNotFound 异常。

1.1.2 take

  • client = Client.take
    take 方法检索一条记录而不考虑排序,等同于:
SELECT * FROM clients LIMIT 1
  • client = Client.take(2)
    take 方法接受数字作为参数,并返回不超过指定数量的查询结果。
SELECT * FROM clients LIMIT 2

1.1.3 first

client = Client.first
first 方法默认查找按主键排序的第一条记录,等同于:

SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1

first 方法接受数字作为参数,并返回不超过指定数量的查询结果
client = Client.first(3)

1.1.4 last 方法

client = Client.last
last 方法默认查找按主键排序的最后一条记录
last 方法接受数字作为参数,并返回不超过指定数量的查询结果

1.1.5 find_by 方法

Client.find_by first_name: 'Lifo'

SELECT * FROM clients WHERE (clients.first_name = 'Lifo') LIMIT 1

1.2 批量检索多个对象

User.all.each 会使 Active Record 一次性取回整个数据表,为每条记录创建模型对象,并把整个模型对象数组保存在内存中。事实上,如果我们有大量记录,整个模型对象数组需要占用的空间可能会超过可用的内存容量。

1.2.1 find_each 方法

每次检索一批记录,然后逐一把每条记录作为模型传入块

User.find_each do |user|
  NewsMailer.weekly(user).deliver_now
end

可选字段::

  • :batch_size 选项用于指明批量检索记录时一次检索多少条记录。例如,一次检索 5000 条记录:
  • :start:配置想要取回的记录序列的第一个 ID,比这个 ID 小的记录都不会取回。
  • :finish:用于配置想要取回的记录序列的最后一个 ID,比这个 ID 大的记录都不会取回。
  • :error_on_ignore:覆盖应用的配置,指定有顺序的关系是否抛出异常。
User.find_each(start: 2000, finish: 10000,batch_size: 5000) do |user|

1.2.2 find_in_batches 方法

把一批记录作为模型数组传入块,而不是像 find_each 方法那样逐一把每条记录作为模型传入块。

Invoice.find_in_batches do |invoices|
  export.add_invoices(invoices)
end

可选字段与find_each相同。

2 条件查询

2.1 纯字符串条件

Client.where("orders_count = ?", params[:orders])

注:避免SQL注入,不推荐以下写法:

Client.where("orders_count = #{params[:orders]}")

2.2 数组条件

Client.where("orders_count = ? AND locked = ?", params[:orders], false)

2.3 散列条件

Client.where(locked: true)
或:
Client.where('locked' => true)

2.4 NOT 条件

Client.where.not(locked: true)

3 排序 order

Client.order(:created_at)
# 或
Client.order("created_at")

还可以使用 ASC(升序) 或 DESC(降序) 指定排序方式:

Client.order(orders_count: :asc, created_at: :desc)
# 或
Client.order(:orders_count, created_at: :desc)
# 或
Client.order("orders_count ASC, created_at DESC")
# 或
Client.order("orders_count ASC", "created_at DESC")

4 选择特定字段

Model.find 默认使用 select * 从结果集中选择所有字段。可以使用 select 方法从结果集中选择字段的子集。

Client.select("viewable_by, locked")

等同于:

SELECT viewable_by, locked FROM clients

在查询时如果想让某个字段的同值记录只出现一次,可以使用 distinct 方法添加唯一性约束:

Client.select(:name).distinct

等同于:

SELECT DISTINCT name FROM clients

5 限量和偏移量

limit 方法用于指明想要取回的记录数量,offset 方法用于指明取回记录时在第一条记录之前要跳过多少条记录。

Client.limit(5).offset(30)

等同于:

SELECT * FROM clients LIMIT 5 OFFSET 30

返回从第 31 条记录开始的 5 条记录。

6 分组

Order.select("date(created_at) as ordered_date, sum(price) as total_price").group("date(created_at)")

根据订单创建日期查找订单记录
注:要想得到一次查询中分组项目的总数,可以在调用 group 方法后调用 count 方法。

Order.group(:status).count

7 having 方法

对查询到的结果,进行条件过滤

Order.select("date(created_at) as ordered_date, sum(price) as total_price"). group("date(created_at)").having("sum(price) > ?", 100)

8 条件覆盖

8.1 unscope 方法

可以使用 unscope 方法删除某些条件。 例如:

Article.where('id > 10').limit(20).order('id asc').unscope(:order)

生成的sql,等同于前面并没有加和order有关的条件过滤

SELECT * FROM articles WHERE id > 10 LIMIT 20

8.2 only 方法

可以使用 only 方法覆盖某些条件。例如:

Article.where('id > 10').limit(20).order('id desc').only(:order, :where)

生成的sql,等同于前面只加了和order,where有关的条件过滤

SELECT * FROM articles WHERE id > 10 ORDER BY id DESC

8.3 reorder 方法

class Article < ApplicationRecord
  has_many :comments, -> { order('posted_at DESC') }
end
Article.find(10).comments.reorder('name')

之前在model层面定义是以posted_at进行排序,后来reorder里指定的是根据name排序,对之前的排序方式进行了覆盖的操作

SELECT * FROM articles WHERE id = 10
SELECT * FROM comments WHERE article_id = 10 ORDER BY name

8.4 reverse_order 方法

可以使用 reverse_order 方法反转排序条件。

User.order(:id)

对应的sql:

SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 11

使用 reverse_order,就可以将本来的ID升序排列反转为降序了

User.order(:id).reverse_order
SELECT `users`.* FROM `users` ORDER BY `users`.`id` DESC LIMIT 11

8.5 rewhere 方法

覆盖 where 方法中指定的条件

Article.where(trashed: true).rewhere(trashed: false)

生成的SQL:

SELECT * FROM articles WHERE `trashed` = 0

9 空关系

none 方法返回可以在链式调用中使用的、不包含任何记录的空关系。

User.none

返回一个空 Relation 对象,(某些特殊情况下不期望返回 [] 或 nil)而且不执行查询
在这里插入图片描述

10 只读对象

显式禁止修改任何返回对象。如果尝试修改只读对象,不但不会成功,还会抛出 ActiveRecord::ReadOnlyRecord 异常。

client = Client.readonly.first

11 联结表

Active Record 提供了 joins 和 left_outer_joins 这两个查找方法,用于指明生成的 SQL 语句中的 JOIN 子句。

11.1 joins 方法

Category.joins(:articles)
Author.joins("INNER JOIN posts ON posts.author_id = authors.id AND posts.published = 't'")

11.2. 单个关联的联结

Category.joins(:articles)

对应的sql

SELECT categories.* FROM categories
  INNER JOIN articles ON articles.category_id = categories.id

11.3 多个关联的联结

Article.joins(:category, :comments)

11.4 单层嵌套关联的联结

Article.joins(comments: :guest)

11.5 多层嵌套关联的联结

Category.joins(articles: [{ comments: :guest }, :tags])

11.6 left_outer_joins 方法

如果想要选择一组记录,而不管它们是否具有关联记录,可以使用 left_outer_joins 方法。

Author.left_outer_joins(:posts).distinct.select('authors.*, COUNT(posts.*) AS posts_count').group('authors.id')

这个查询的意思是返回所有作者和每位作者的帖子数,而不管这些作者是否发过帖子。即左外联只要左表有记录,就将右表中与之对应的结果查询出来,而内联是坐表和右表都对应的有记录且数据值符合条件。

12 及早加载关联

Article.includes(:category, :comments).limit(10)

通过指明 includes 方法,Active Record 会使用尽可能少的查询来加载所有已指明的关联。从而解决n+1问题

13 作用域

在类中通过 scope 方法定义作用域,并传入调用这个作用域时执行的查询。

class Article < ApplicationRecord
  scope :published, -> { where(published: true) }
end

class Article < ApplicationRecord
  def self.published
    where(published: true)
  end
end

我们可以在模型上调用 published 作用域:

Article.published # => [published articles]

13.1 传入参数

作用域可以接受参数:

class Article < ApplicationRecord
  scope :created_before, ->(time) { where("created_at < ?", time) }
end

或:

class Article < ApplicationRecord
  def self.created_before(time)
    where("created_at < ?", time)
  end
end

调用

Article.created_before(Time.zone.now)

当作用域需要接受参数时,推荐改用类方法(也就是通过self定义的方法)。使用类方法时,这些方法仍然可以在关联对象上访问

13.2 应用默认作用域

要想在模型的所有查询中应用作用域,我们可以在这个模型上使用 default_scope 方法。默认作用域在创建记录时同样起作用,但在更新记录时不起作用

class Client < ApplicationRecord
  default_scope { where("removed_at IS NULL") }
end

14 动态查找方法

如果想同时查询 first_name 和 locked 字段,可以在动态查找方法中用 and 把这两个字段连起来,

Client.find_by_first_name_and_locked("Ryan", true)

15 理解方法链

15.1 从多个数据表中检索过滤后的数据

Person
  .select('people.id, people.name, comments.text')
  .joins(:comments)
  .where('comments.created_at > ?', 1.week.ago)

15.2 从多个数据表中检索特定的数据

Person
  .select('people.id, people.name, companies.name')
  .joins(:company)
  .find_by('people.name' => 'John') # this should be the last

16 查找或创建新对象

我们经常需要查找记录并在找不到记录时创建记录,这时我们可以使用 find_or_create_by 和 find_or_create_by! 方法。

16.1 find_or_create_by 方法

find_or_create_by 方法检查具有指定属性的记录是否存在。如果记录不存在,就调用 create 方法创建记录。

Client.find_or_create_by(first_name: 'Andy')

假设我们想在新建记录时把 locked 字段设置为 false,但又不想在查询中进行设置。

  • 使用 create_with
Client.create_with(locked: false).find_or_create_by(first_name: 'Andy')
  • 使用块
Client.find_or_create_by(first_name: 'Andy') do |c|
 c.locked = false
end

16.2 find_or_create_by! 方法

新建记录是无效的就会抛出异常

16.3 find_or_initialize_by 方法

find_or_initialize_by 方法的工作原理和 find_or_create_by 方法类似,区别之处在于前者调用的是 new 方法而不是 create 方法。这意味着新建模型实例在内存中创建,但没有保存到数据库。要想把 对象保存到数据库,还需调用 save 方法:

17 使用 SQL 语句进行查找

直接使用 SQL 语句在数据表中查找记录

Client.find_by_sql("SELECT * FROM clients")

17.1 select_all 方法

select_all 方法也会使用定制的 SQL 语句从数据库中检索对象,区别在于 select_all 方法不会对这些对象进行实例化,而是返回一个散列构成的数组,其中每个散列表示一条记录。
在这里插入图片描述

17.2 pluck 方法

pluck 方法用于在模型对应的底层数据表中查询单个或多个字段。它接受字段名的列表作为参数,并返回这些字段的值的数组.
Client.pluck(:id, :name)

SELECT clients.id, clients.name FROM clients

和 select 方法不同,pluck 方法把数据库查询结果直接转换为 Ruby 数组,而不是构建 Active Record 对象。
在这里插入图片描述

17.3 ids 方法

Person.ids获得关联的所有 ID,也就是数据表的主键。

18 检查对象是否存在 exists?

使用 exists? 方法
Client.exists?(1)

Client.where(first_name: 'Ryan').exists?

19 计算

  • average 方法:数据表中某个字段的平均值Client.average("orders_count")
  • minimum:查找数据表中某个字段的最小值Client.minimum("age")
  • maximum:数据表中某个字段的最大值Client.maximum("age")
  • sum:某个字段的所有字段值之和Client.sum("orders_count")
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当使用Rails框架编写接口时,可以按照以下步骤进行: 1. 创建一个新的Rails应用程序: 打开终端,导航到项目目录并运行以下命令: ``` rails new api_example cd api_example ``` 2. 创建一个控制器: 运行以下命令创建一个控制器: ``` rails generate controller Api ``` 3. 在控制器中定义接口方法: 打开 `app/controllers/api_controller.rb` 文件,添加如下方法: ```ruby class ApiController < ApplicationController def hello render json: { message: 'Hello, World!' } end def greet name = params[:name] render json: { message: "Hello, #{name}!" } end end ``` 4. 配置路由: 打开 `config/routes.rb` 文件,添加以下路由配置: ```ruby Rails.application.routes.draw do get '/api/hello', to: 'api#hello' post '/api/greet', to: 'api#greet' end ``` 5. 运行应用程序: 在终端中运行以下命令启动Rails服务器: ``` rails server ``` 现在,你的Rails应用程序就有了两个接口:`/api/hello` 和 `/api/greet`。 - GET请求的 `/api/hello` 接口返回一个JSON响应,包含一条简单的问候消息。 - POST请求的 `/api/greet` 接口从请求参数中获取名字,并返回一个包含问候消息的JSON响应。 你可以使用REST客户端(如Postman)或浏览器来访问这些接口,例如: - GET请求:`http://localhost:3000/api/hello` - POST请求:`http://localhost:3000/api/greet`,并在请求体中添加名为`name`的参数。 这样,你就可以使用Rails框架创建一个简单的接口,处理不同的HTTP请求并返回相应的JSON响应。当然,在实际开发中,可能会涉及更复杂的数据处理和业务逻辑,但这个例子可以帮助你快速入门。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值