gpt4 book ai didi

ruby-on-rails - Rails Scope返回all而不是nil

转载 作者:行者123 更新时间:2023-12-03 09:04:21 24 4
gpt4 key购买 nike

我在创建范围和使用first查找器时遇到了一个奇怪的问题。似乎在范围内使用first作为查询的一部分将使它在没有找到结果的情况下返回所有结果。如果找到任何结果,它将正确返回第一个结果。

我已经设置了一个非常简单的测试来演示这一点:

class Activity::MediaGroup < ActiveRecord::Base
scope :test_fail, -> { where('1 = 0').first }
scope :test_pass, -> { where('1 = 1').first }
end

请注意,对于此测试,我已经设置了匹配记录或不记录的条件。实际上,我是根据实际条件查询的,并且得到相同的奇怪行为。

这是失败范围的结果。如您所见,它会进行正确的查询,没有任何结果,因此它将查询所有匹配的记录并返回该记录:
irb(main):001:0> Activity::MediaGroup.test_fail
Activity::MediaGroup Load (0.0ms) SELECT "activity_media_groups".* FROM "activity_media_groups" WHERE (1 = 0) ORDER BY "activity_media_groups"."id" ASC LIMIT 1
Activity::MediaGroup Load (0.0ms) SELECT "activity_media_groups".* FROM "activity_media_groups"
=> #<ActiveRecord::Relation [#<Activity::MediaGroup id: 1, created_at: "2014-01-06 01:00:06", updated_at: "2014-01-06 01:00:06", user_id: 1>, #<Activity::MediaGroup id: 2, created_at: "2014-01-06 01:11:06", updated_at: "2014-01-06 01:11:06", user_id: 1>, #<Activity::MediaGroup id: 3, created_at: "2014-01-06 01:26:41", updated_at: "2014-01-06 01:26:41", user_id: 1>, #<Activity::MediaGroup id: 4, created_at: "2014-01-06 01:28:58", updated_at: "2014-01-06 01:28:58", user_id: 1>]>

另一个作用域按预期运行:
irb(main):002:0> Activity::MediaGroup.test_pass
Activity::MediaGroup Load (1.0ms) SELECT "activity_media_groups".* FROM "activity_media_groups" WHERE (1 = 1) ORDER BY "activity_media_groups"."id" ASC LIMIT 1
=> #<Activity::MediaGroup id: 1, created_at: "2014-01-06 01:00:06", updated_at: "2014-01-06 01:00:06", user_id: 1>

如果在范围之外执行相同的逻辑,则会得到预期的结果:
irb(main):003:0> Activity::MediaGroup.where('1=0').first
Activity::MediaGroup Load (0.0ms) SELECT "activity_media_groups".* FROM "activity_media_groups" WHERE (1=0) ORDER BY "activity_media_groups"."id" ASC LIMIT 1
=> nil

我在这里想念什么吗?在我看来,这似乎是Rails / ActiveRecord / Scopes中的错误,除非我没有意识到某些未知的行为期望。

最佳答案

经过一些研究后,我发现它的是有目的的设计的,这不是错误或怪异。

首先,

  • scope返回一个ActiveRecord::Relation
  • 如果记录为零,则将其编程为返回所有记录
    再次是ActiveRecord::Relation而不是nil

  • 这背后的想法是使 范围可链接(即) scopeclass methods之间的 键差之一

    示例:

    让我们使用以下情形:用户将能够按状态过滤帖子,并按最近更新的帖子排序。足够简单,让我们为此编写作用域:
    class Post < ActiveRecord::Base
    scope :by_status, -> status { where(status: status) }
    scope :recent, -> { order("posts.updated_at DESC") }
    end

    我们可以这样自由地调用它们:
    Post.by_status('published').recent
    # SELECT "posts".* FROM "posts" WHERE "posts"."status" = 'published'
    # ORDER BY posts.updated_at DESC

    或使用用户提供的参数:
    Post.by_status(params[:status]).recent
    # SELECT "posts".* FROM "posts" WHERE "posts"."status" = 'published'
    # ORDER BY posts.updated_at DESC

    到现在为止还挺好。现在,为了比较,将它们移到类方法中:
    class Post < ActiveRecord::Base
    def self.by_status(status)
    where(status: status)
    end

    def self.recent
    order("posts.updated_at DESC")
    end
    end

    除了使用一些额外的行,没有太大的改进。但是,现在如果:status参数为nil或空白怎么办?
    Post.by_status(nil).recent
    # SELECT "posts".* FROM "posts" WHERE "posts"."status" IS NULL
    # ORDER BY posts.updated_at DESC

    Post.by_status('').recent
    # SELECT "posts".* FROM "posts" WHERE "posts"."status" = ''
    # ORDER BY posts.updated_at DESC

    糟糕,我认为我们不想允许这些查询,对吗?使用范围,我们可以通过在我们的范围中添加一个存在条件来轻松解决此问题:
    scope :by_status, -> status { where(status: status) if status.present? }

    我们去了:
    Post.by_status(nil).recent
    # SELECT "posts".* FROM "posts" ORDER BY posts.updated_at DESC

    Post.by_status('').recent
    # SELECT "posts".* FROM "posts" ORDER BY posts.updated_at DESC

    太棒了现在,让我们尝试使用我们钟爱的类方法做同样的事情:
    class Post < ActiveRecord::Base
    def self.by_status(status)
    where(status: status) if status.present?
    end
    end

    运行此:
    Post.by_status('').recent
    NoMethodError: undefined method `recent' for nil:NilClass

    和:炸弹:。区别在于,范围将始终返回关系,而我们的简单类方法实现则不会。类方法应如下所示:
    def self.by_status(status)
    if status.present?
    where(status: status)
    else
    all
    end
    end

    请注意,我将为nil / blank情况返回所有内容,在Rails 4中会返回一个关系(它先前从数据库中返回了项数组)。在Rails 3.2.x中,应该在此处使用范围。然后我们去:
    Post.by_status('').recent
    # SELECT "posts".* FROM "posts" ORDER BY posts.updated_at DESC

    因此,这里的建议是:永远不要从应该像作用域一样工作的类方法中返回nil,否则您将打破作用域所隐含的可链接性条件,该条件总是返回一个关系。

    长话短说:

    无论如何,作用域旨在返回 ActiveRecord::Relation以使其可链接。如果您期望 firstlastfind结果,则应使用 class methods
    资料来源: http://blog.plataformatec.com.br/2013/02/active-record-scopes-vs-class-methods/

    关于ruby-on-rails - Rails Scope返回all而不是nil,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20942672/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com