Have a Post Model with an Enum
// Post.rb
enum category: {
job: 'job',
conference: 'conference'
}
and an Org has and belongs to many Posts (via join table)
Org.includes(:posts).where(category: Post.categories[:job])
appears that its trying to call category on Org. Is there a way to write this to only return the Org with included post, where the post has an enum of string 'job'?
tl;dr use
Org.includes(:posts).where(posts: {category: :job})
The longer answer...
I guess it's worth noting that your question doesn't really have anything to do with enums. Nor does it have to do with "including another model". What you're really trying to do is Specify Conditions on the Joined Tables which you can read more about in the Active Record Query Interface guide.
The issues is that you've malformed your ActiveRecord Query:
Org.includes(:posts).where(category: Post.categories[:job])
The essential form of what you currently have is:
Model.where(attribute: 'value')
The:
.includes(:joined_models)
...bit doesn't change the essential form. And so, ActiveRecord will return all Model records where attribute has value. Or, in your case, all Org models where category is job.
But, that's not what you want. You want all Orgs that have Posts where the Post category is job. (Or, I suppose, "all Orgs with job posts.")
Which is where the .includes(:joined_models) bit comes in: it lets you specify conditions on joined_models, which in it's essential form looks like:
Model.includes(:joined_models).where(joined_models: {attribute: 'value'})
^^^^^^^^^^^^^
Or, in your case:
Org.includes(:posts).where(posts: {category: Post.categories[:job]})
Or, as mu says in the comments:
Org.includes(:posts).where(posts: {category: :job})
Now, I don't know what context you're in, but wherever you are, that above code requires that your context knows a lot about Org and how it relates to Post and the attributes of Post which in general is not great. So, I suggest you add a method to Org which allows you to decouple the knowledge of Org in your context:
class Org < ApplicationRecord
class << self
def with_job_posts
includes(:posts).where(posts: {category: :job}})
end
end
end
And now you can simply do:
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 also has a category of conference. So, you could do:
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
But, if your Post categories start to grow, that will become tedious. So, instead do:
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
And now you'll have any number of methods like:
Org.with_job_posts
Org.with_conference_posts
Org.with_some_other_type_of_posts
Super! Check out this Q&A for more info from Jörg W Mittag.
BTW, that looks like a potentially unusual way to use enum. In the docs, it says:
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
I always figured a mapped enum was meant to use integers as the values, not strings. Interesting.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With