【问题标题】:find vs find_by vs where查找 vs find_by vs where
【发布时间】:2012-06-22 17:58:10
【问题描述】:

我是 Rails 新手。我看到有很多方法可以找到记录:

  1. find_by_<columnname>(<columnvalue>)
  2. find(:first, :conditions => { <columnname> => <columnvalue> }
  3. where(<columnname> => <columnvalue>).first

看起来它们最终都生成了完全相同的 SQL。另外,我相信查找多条记录也是如此:

  1. find_all_by_<columnname>(<columnvalue>)
  2. find(:all, :conditions => { <columnname> => <columnvalue> }
  3. where(<columnname> => <columnvalue>)

对于使用哪一个有经验法则或建议吗?

【问题讨论】:

    标签: ruby-on-rails rails-activerecord dynamic-finders


    【解决方案1】:

    哪里返回 ActiveRecord::Relation

    现在看看 find_by 的实现:

    def find_by
      where(*args).take
    end
    

    如您所见,find_bywhere 相同,但它只返回一条记录。此方法应该用于获取 1 条记录,where 应该用于获取具有某些条件的所有记录。

    【讨论】:

    • find_by 返回一个对象,而 where 返回一个集合。
    • 当查询值超出范围时,find_by 将从where(*args) 中救出::RangeError 并返回nil。
    【解决方案2】:

    编辑: 这个答案很老,自从发表这篇文章以来,已经出现了其他更好的答案。我建议您查看@Hossam Khamis 在下面发布的详细信息。

    使用您认为最适合您需要的任何一种。

    find 方法通常用于通过 ID 检索一行:

    Model.find(1)
    

    值得注意的是,如果您提供的属性未找到该项目,find 将引发异常。使用where(如下所述,如果没有找到该属性将返回一个空数组)以避免抛出异常。

    find 的其他用途通常被替换为如下内容:

    Model.all
    Model.first
    

    find_by 在您搜索列中的信息时用作帮助器,它通过命名约定映射到此类。例如,如果您的数据库中有一个名为 name 的列,您将使用以下语法:

    Model.find_by(name: "Bob")
    

    .where 更像是一个包罗万象的方法,它可以让您使用更复杂的逻辑来处理传统助手无法执行的操作,它会返回一个符合您的条件的项目数组(否则返回一个空数组)。

    【讨论】:

    • find_by 没有被弃用,但语法发生了一些变化。从find_by_name("Bob")find_by(:name, "Bob")
    • @BrianMorearty 我相信你的意思是find_by(name: "Bob")
    • @BrianMorearty 我找不到任何find_by_... 被弃用的证据,你有消息来源吗? Rails 4 似乎仍然支持 find_byfind_by_...
    • @Dennis,你说得对,它没有被弃用,这就是我所说的。但是当我说“但语法正在发生一些变化”时,我本可以更清楚。我的意思是,“但现在也有新的语法可用。”请参阅 MCB 对新语法的更正。
    • 这是 rails 4.0 版本中提到的“所有动态方法,除了 find_by_... 和 find_by_...!已弃用”edgeguides.rubyonrails.org/… 的更多详细信息
    【解决方案3】:

    Model.find

    1- 参数:要查找的对象的ID。

    2- 如果找到:它返回对象(仅一个对象)。

    3- 如果未找到:引发ActiveRecord::RecordNotFound 异常。

    Model.find_by

    1- 参数:key/value

    例子:

    User.find_by name: 'John', email: 'john@doe.com'
    

    2- 如果找到:它返回对象。

    3- 如果未找到:返回 nil

    注意:如果你想让它提高 ActiveRecord::RecordNotFound 使用 find_by!

    Model.where

    1- 参数:同find_by

    2- 如果找到:返回ActiveRecord::Relation,其中包含一条或多条与参数匹配的记录。

    3- 如果没有找到:它返回一个 Empty ActiveRecord::Relation

    【讨论】:

      【解决方案4】:

      findfind_by 之间的区别在于,find 将在未找到时返回错误,而 find_by 将返回 null。

      有时,如果您有 find_by email: "haha" 之类的方法,而不是 .where(email: some_params).first,则更容易阅读。

      【讨论】:

        【解决方案5】:

        从 Rails 4 开始,您可以这样做:

        User.find_by(name: 'Bob')
        

        这相当于 Rails 3 中的 find_by_name

        #find#find_by 不够用时使用#where

        【讨论】:

        • Agis,我同意你的观点,但我一直在互联网上搜索我们为什么要使用 find_by 而不是 find_by_<column_name>。我需要它来回答某人。
        • Agis,您是否有任何来源支持您的主张,即我们不应再在 Rails 4 中使用 find_by_name?据我所知,it's not been deprecated.
        • 有没有一种简单的方法可以做到这一点,但说名称中有一个通配符,比如find_by(name: "Rob*")
        • @Dennis 两者都可以使用,它们是有效的。我更喜欢新语法,因为 API 更直观,恕我直言。这就是我自己设计的方式:)
        【解决方案6】:

        接受的答案通常涵盖了所有内容,但我想补充一点, 以防您打算以更新的方式使用模型,并且您正在检索单个记录(您不知道其id),然后find_by 是要走的路,因为它检索记录和不把它放在一个数组中

        irb(main):037:0> @kit = Kit.find_by(number: "3456")
          Kit Load (0.9ms)  SELECT "kits".* FROM "kits" WHERE "kits"."number" = 
         '3456' LIMIT 1
        => #<Kit id: 1, number: "3456", created_at: "2015-05-12 06:10:56",   
        updated_at: "2015-05-12 06:10:56", job_id: nil>
        
        irb(main):038:0> @kit.update(job_id: 2)
        (0.2ms)  BEGIN Kit Exists (0.4ms)  SELECT 1 AS one FROM "kits" WHERE  
        ("kits"."number" = '3456' AND "kits"."id" != 1) LIMIT 1 SQL (0.5ms)   
        UPDATE "kits" SET "job_id" = $1, "updated_at" = $2 WHERE  "kits"."id" = 
        1  [["job_id", 2], ["updated_at", Tue, 12 May 2015 07:16:58 UTC +00:00]] 
        (0.6ms)  COMMIT => true
        

        但是如果你使用where那么你不能直接更新它

        irb(main):039:0> @kit = Kit.where(number: "3456")
        Kit Load (1.2ms)  SELECT "kits".* FROM "kits" WHERE "kits"."number" =  
        '3456' => #<ActiveRecord::Relation [#<Kit id: 1, number: "3456", 
        created_at: "2015-05-12 06:10:56", updated_at: "2015-05-12 07:16:58", 
        job_id: 2>]>
        
        irb(main):040:0> @kit.update(job_id: 3)
        ArgumentError: wrong number of arguments (1 for 2)
        

        在这种情况下,您必须像这样指定它

        irb(main):043:0> @kit[0].update(job_id: 3)
        (0.2ms)  BEGIN Kit Exists (0.6ms)  SELECT 1 AS one FROM "kits" WHERE 
        ("kits"."number" = '3456' AND "kits"."id" != 1) LIMIT 1 SQL (0.6ms)   
        UPDATE "kits" SET "job_id" = $1, "updated_at" = $2 WHERE "kits"."id" = 1  
        [["job_id", 3], ["updated_at", Tue, 12 May 2015 07:28:04 UTC +00:00]]
        (0.5ms)  COMMIT => true
        

        【讨论】:

        • 正是我想要的
        • @kit = Kit.where(number: "3456").first - 这可以让您直接更新它,并且它更安全,因为它在弃用中幸存下来
        【解决方案7】:

        除了接受的答案,以下也是有效的

        Model.find() 可以接受 id 数组,并将返回所有匹配的记录。 Model.find_by_id(123) 也接受数组,但只会处理数组中存在的第一个 id 值

        Model.find([1,2,3])
        Model.find_by_id([1,2,3])
        

        【讨论】:

          【解决方案8】:

          目前给出的答案都OK。

          然而,一个有趣的区别是Model.find 按 id 搜索;如果找到,它会返回一个 Model 对象(只是一条记录),否则会抛出一个 ActiveRecord::RecordNotFound

          Model.find_byModel.find 非常相似,可让您搜索数据库中的任何列或列组,但如果没有记录与搜索匹配,则返回 nil

          另一方面,

          Model.where 返回一个Model::ActiveRecord_Relation 对象,它就像一个包含与搜索匹配的所有记录 的数组。如果没有找到记录,则返回一个空的Model::ActiveRecord_Relation 对象。

          我希望这些可以帮助您决定在任何时候使用哪个。

          【讨论】:

            【解决方案9】:

            假设我有一个模型User

            1. User.find(id)

            返回主键 = id 的行。返回类型将是User 对象。

            1. User.find_by(email:"abc@xyz.com")

            在这种情况下,返回具有匹配属性或电子邮件的第一行。返回类型将再次为 User 对象。

            注意:- User.find_by(email: "abc@xyz.com") 类似于 User.find_by_email("abc@xyz.com")

            1. User.where(project_id:1)

            返回用户表中属性匹配的所有用户。

            这里的返回类型将是ActiveRecord::Relation 对象。 ActiveRecord::Relation 类包含 Ruby 的 Enumerable 模块,因此您可以像数组一样使用它的对象并对其进行遍历。

            【讨论】:

              【解决方案10】:

              您列表中的两个 #2 均已弃用。不过你仍然可以使用find(params[:id])

              通常,where() 在大多数情况下都有效。

              这是一个很棒的帖子:https://web.archive.org/web/20150206131559/http://m.onkey.org/active-record-query-interface

              【讨论】:

              【解决方案11】:

              使用任何开源技术的最佳部分是您可以检查它的长度和广度。 Checkout this link

              find_by ~> 查找符合指定条件的第一条记录。没有隐含的顺序,所以如果顺序很重要,您应该自己指定。如果没有找到记录,则返回 nil。

              find ~> 找到符合指定条件的第一条记录,但如果没有找到记录,则会引发异常,但这是故意的。

              请查看上面的链接,它包含以下两个功能的所有解释和用例。

              【讨论】:

                【解决方案12】:

                我个人会推荐使用

                where(< columnname> => < columnvalue>)
                

                【讨论】:

                • 这可能会回答这个问题。但请尝试描述使用您的方法的利弊,而不是接受的答案。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-01-30
                • 1970-01-01
                • 2010-09-12
                • 2016-04-23
                • 2016-01-09
                相关资源
                最近更新 更多