【问题标题】:ActiveRecord: How to include another model based on Enum?ActiveRecord:如何包含另一个基于 Enum 的模型?
【发布时间】:2019-10-29 23:02:17
【问题描述】:

有一个带有枚举的帖子模型

// Post.rb

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

一个组织拥有并属于许多帖子(通过连接表)

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

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

【问题讨论】:

    标签: ruby-on-rails ruby enums rails-activerecord


    【解决方案1】:

    tl;dr 使用

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

    更长的答案...

    我想值得注意的是,您的问题与enums 没有任何关系。它也与“包括另一个模型”无关。您真正想做的是Specify Conditions on the Joined Tables,您可以在Active Record Query Interface guide 中了解更多信息。

    问题是您的 ActiveRecord 查询格式错误:

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

    你目前拥有的基本形式是:

    Model.where(attribute: 'value')
    

    .includes(:joined_models)
    

    ...bit 不会改变基本形式。因此,ActiveRecord 将返回所有Model 记录,其中attribute 具有value。或者,在您的情况下,所有Org 模型,其中categoryjob

    但是,这不是你想要的。您想要所有具有PostsOrgs,其中Post categoryjob。 (或者,我想,“所有有职位的组织。”)

    .includes(:joined_models) 位出现在哪里:它允许您在 joined_models 上指定条件,其基本形式如下:

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

    或者,在你的情况下:

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

    或者,正如 mu 在 cmets 中所说:

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

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

    class Org < ApplicationRecord 
    
      class << self
    
        def with_job_posts
          includes(:posts).where(posts: {category: :job}})
        end
    
      end
    
    end
    

    现在你可以简单地做:

    Org.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 以获取来自Jörg W Mittag 的更多信息。

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

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

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

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

    【讨论】:

    • 你应该可以选择Org.includes(:posts).where(posts: {category: :job}),不是吗?而且我经常将字符串与枚举一起使用,这使得 ActiveRecord 之外的事情更容易理解。
    • @muistooshort - 绝对。我坚持 OP 的方式“只是因为”。如果真的要硬编码,那么您的方式就是正确的方式。我将不得不考虑使用字符串而不是整数。我从来没有想过。也许是我从未在 ActiveRecord 之外生活过的神器。
    • 我一直生活在 AR 之外,并且经常使用多种工具访问数据库,因此我讨厌 serialize、编号枚举、STI 和其他将你与 AR 联系在一起的“简单”事物。
    猜你喜欢
    • 1970-01-01
    • 2012-08-15
    • 1970-01-01
    • 2013-08-27
    • 2018-04-15
    • 1970-01-01
    • 2017-08-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多