ActiveRecord:如何根据枚举值包含另一个模型?

3

使用枚举(Enum)创建一个Post模型

// Post.rb

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

一个组织(Org)可以拥有并关联到多个职位(Posts)(通过连接表)

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

看起来它试图在 Org 上调用 category。有没有一种方法只返回包含带有字符串“ job”枚举的 post 的 Org,而同时也包含了 post?

1个回答

3

tl;dr使用

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

更长的答案...

我想值得注意的是,你的问题实际上与enums没有任何关系。也不涉及“包含另一个模型”。你真正想做的是指定连接表上的条件,你可以在Active Record查询接口指南中了解更多信息。

问题在于你的ActiveRecord查询语句格式不正确:

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

您目前所拥有的基本形式是:


Model.where(attribute: 'value')

这个:

.includes(:joined_models)

这段代码对基本形式没有改变。因此,ActiveRecord将返回所有Model记录,其中attribute具有value 。或者,在您的情况下,返回所有categoryjobOrg模型。

但这不是您想要的。您想要所有拥有PostsOrgs,其中Post categoryjob。(或者,我想说的是,“所有带有职位帖子的Orgs。”)

这就是.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

我希望你能翻译以下内容: "...and get back "all Orgs with job posts". And your context needs to know a lot less about Post and its attributes."

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

但是,如果您的帖子 类别开始增多,那么这将变得很繁琐。因此,建议采用以下方式:

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

太棒了!查看这个问答以获取来自Jörg W Mittag的更多信息。

顺便说一下,那似乎是使用enum的潜在不同寻常的方式。在文档中,它说:

最后,也可以使用哈希明确映射属性和数据库整数之间的关系:

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

我一直认为映射枚举应该使用整数作为值,而不是字符串。有趣。


1
你应该可以使用 Org.includes(:posts).where(posts: {category: :job}),是吗?我经常使用枚举字符串,这样在ActiveRecord外部理解起来会更容易。 - mu is too short
1
@muistooshort - 当然。我之前一直按照原帖的方式进行,只是因为想保持一致。如果真的需要硬编码,你的方式是正确的。至于使用字符串而不是整数的那一点,我需要再考虑一下。这可能是因为我从来没有离开过ActiveRecord的缘故。 - jvillian
2
我一直生活在AR之外,并经常使用多种工具访问数据库,因此我不喜欢像“序列化”,编号枚举,STI和其他“简单”事物,这些会将你与AR绑定在一起。 - mu is too short

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接