ActiveRecord:如何包含另一个基于 Enum 的模型?

ActiveRecord: How to include another model based on Enum?

有一个 Post 带有枚举的模型

// Post.rb

enum category: {
   job: 'job',
   conference: 'conference'
}

一个组织拥有并属于许多 Post 个(通过加入 table)

Org.includes(:posts).where(category: Post.categories[:job])

它似乎在尝试调用 Org 上的类别。有没有办法只写这个 return 包含 post 的组织,其中 post 有一个字符串枚举 'job'?

tl;dr 使用

Org.includes(:posts).where(posts: {category: :job})

较长的答案...

我想值得注意的是,您的问题实际上与 enums 没有任何关系。它也与 "including another model" 无关。你真正想做的是 Specify Conditions on the Joined Tables which you can read more about in the Active Record Query Interface guide.

问题是您的 ActiveRecord 查询格式不正确:

Org.includes(:posts).where(category: Post.categories[:job])

您当前拥有的基本形式是:

Model.where(attribute: 'value')

那个:

.includes(:joined_models)

...位不会改变基本形式。因此,ActiveRecord 将 return 所有 Model 条记录,其中 attributevalue。或者,在您的情况下,categoryjob 的所有 Org 模型。

但是,这不是你想要的。您想要所有具有 PostsOrgs,其中 Post categoryjob。 (或者,我想,"all Orgs with job posts.")

这就是 .includes(:joined_models) 位的用武之地:它允许您在 joined_models 上指定条件,其基本形式如下所示:

Model.includes(:joined_models).where(joined_models: {attribute: 'value'})
                                     ^^^^^^^^^^^^^

或者,在您的情况下:

Org.includes(:posts).where(posts: {category: Post.categories[:job]})

或者,正如 mu 在评论中所说:

Org.includes(:posts).where(posts: {category: :job})

现在,我不知道你在什么上下文中,但无论你在哪里,上面的代码都要求你的上下文了解很多关于 Org 以及它与 Post 的关系和 Post 的属性一般来说不是很好。所以,我建议你在 Org 中添加一个方法,它允许你在你的上下文中解耦 Org 的知识:

class Org < ApplicationRecord 

  class << self

    def with_job_posts
      includes(:posts).where(posts: {category: :job}})
    end

  end

end

现在你可以简单地做:

Org.with_job_posts

...然后返回 "all Orgs with job posts"。而且您的上下文需要了解的关于 Post 及其属性的信息要少得多。

Post 也有一个类别 conference。所以,你可以这样做:

class Org < ApplicationRecord 

  class << self

    def with_job_posts
      includes(:posts).where(posts: {category: :job}})
    end

    def with_conference_posts
      includes(:posts).where(posts: {category: :conference}})
    end

  end

end

但是,如果您的 Post categories 开始增长,那将变得乏味。所以,改为:

class Org < ApplicationRecord 

  Post.categories.each do |post_category|
    define_singleton_method("#{post_category}"_posts) do 
      includes(:posts).where(posts: {category: post_category.to_sym})
    end
  end

end

现在您将拥有任意数量的方法,例如:

Org.with_job_posts
Org.with_conference_posts
Org.with_some_other_type_of_posts

超级棒!查看 this Q&A for more info from Jörg W Mittag

顺便说一句,这看起来像是一种使用 enum 的不寻常方式。在 docs 中,它表示:

Finally, it's also possible to explicitly map the relation between attribute and database integer with a hash:

class Conversation < ActiveRecord::Base
  enum status: { active: 0, archived: 1 }
end

我一直认为映射枚举意味着使用整数作为值,而不是字符串。有意思。