I have models like below:
class Foo < ActiveRecord::Base
has_many: :bars
end
class Bar < ActiveRecord::Base
belongs_to: :foo
has_many: :bazs
end
class Baz < ActiveRecord::Base
belongs_to: :bar
end
How can I can includes foo in my baz query? (Something like Baz.includes(:foo).where(condition: 'condition').map(&:foo))
You'll have to get a join to foo through the bar association. Something similar to this should work for you in ActiveRecord.
Baz.joins(bar: :foo).where(foos: { SOME_FOO_COLUMN: 'condition' })
This will return a collection of Baz's where your Foo condition is true.
Related
How to get the queried data having polymorphic association
I have an 3 models
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Employee < ApplicationRecord
has_many :pictures, as: :imageable
end
class Product < ApplicationRecord
has_many :pictures, as: :imageable
end```
Employee and Product have column **is_active**.
In rails admin dropdown. I want to display the data where employee and product is **is_active = true.**
Have tried multiple ways to achieve this.
Please let me know if there is any solution?
You can write a custom scope in picture model as below
scope :list_active(imageable_type), -> {
where(imageable_type: imageable_type)
.joins("INNER JOIN #{imageable_type.pluralize} ON
{#imageable_type.pluralize}.id = imageable_id AND
imageable_type = '#{imageable_type}'")
.where('#{imageable_type.pluralize}.is_active = ?', true)
}
Then you can simply list and use the response.
E.g result = []
result << Image.list_active('Employee')
result << Image.list_active('Product')
Let's say I have an Item model and Category model with has_many :through association:
class Item < ActiveRecord::Base
has_many :category_items
has_many :categories, through: category_items
end
class Category < ActiveRecord::Base
has_many :category_items
has_many :items, through: category_items
end
class CategoryItems < ActiveRecord::Base
belongs_to :category
belongs_to :items
end
now, I want to have a scope on items that will get all items that are in specific status (assume it has status attribute) for specific category. for example: get all items with status "in stock" and which belongs to category with id = 3, something like:
scope :in_stock_for_category, ->(category) { where(status: SOME_ENUMERATED_VALUE) ....
i'm missing the last part of the query to limit the result set to the specific category.
Thanks!
Since you don't have a category_id column in your items table, you need to join either category_items or cateogeries in your scope before you can specify a particular category's ID condition.
class Item < ActiveRecord::Base
scope :in_stock_for_category, -> do |category|
joins(:category_items).
where(category_items: {category_id: category.id}).
where(items: {status: SOME_ENUMERATED_VALUE}).
group("items.id") # grouping might be unnecessary since you're adding the where condition for the category's id
end
end
That will work. Or if you want to join categories, do the following:
class Item < ActiveRecord::Base
scope :in_stock_for_category, -> do |category|
joins(:categories).
where(categories: {id: category.id}).
where(items: {status: SOME_ENUMERATED_VALUE}).
group("items.id") # grouping might be unnecessary since you're adding the where condition for the category's id
end
end
If you already have a category however, it might be useful to create a has_many relationship for a items that have a certain status. Something like the following:
class Category < ActiveRecord::Base
has_many :in_stock_items, -> do
where(items: {status: SOME_ENUMERATED_VALUE})
end, through: :category_items, source: :item
end
Also, if you have a status scope in Item (something like scope :in_stock, -> { where(status: SOME_ENUMERATED_VALUE) }), you can most likely change the above has_many relationship to the following:
class Category < ActiveRecord::Base
has_many :in_stock_items, -> do
merge(Item.in_stock) # http://apidock.com/rails/ActiveRecord/SpawnMethods/merge
end, through: :category_items, source: :item
end
That should tidy things up.
I have a Product class that has_many Gender through Connection class instances. I want to query to find products that have both end_a and end_b present. The current class method works with 2 caveats:
Fails to return correctly if searching where end_a and end_b are the same. Instead should search if product has 2 instances, not just one of object.
Returns an Array when I want an ActiveRecord_Relation.
The class method .query is below, any feedback or ideas are appreciated.
class Product < ActiveRecord::Base
has_many :connections, dependent: :destroy, as: :connectionable
has_many :genders, through: :connections
def self.query(end_a, end_b)
search_base = active.joins(:connections)
end_a_search = search_base.where(connections: { gender_id: end_a } )
end_a_search & search_base.where(connections: { gender_id: end_b } )
end
end
ps: Once this is figured out will likely move this to a scope for Product
class Product < ActiveRecord::Base
has_many :connections, dependent: :destroy, as: :connectionable
has_many :genders, through: :connections
scope :with_genders, -> (end_a, end_b) {
relation = joins('INNER JOIN connections c1 ON c1.connectionable_id = products.id AND c1.connectionable_type = \'Product\'')
.joins('INNER JOIN connections c2 ON c1.connectionable_id = c2.connectionable_id AND c2.connectionable_type = \'Product\'')
.where(c1: {gender_id: end_a}, c2: {gender_id: end_b})
.group('products.id')
end_a == end_b ? relation.having('COUNT(products.id) > 1') : relation
}
end
I'm trying to figure out what is happening when I create a has_many through association.
models:
class Foo
has_many :bars
has_many :bazes, through: :bars
acceptes_nested_attributes_for :bars
acceptes_nested_attributes_for :bazes
end
class Bar
belongs_to :foo
belongs_to :baz
before_create :update_some_attr
def update_some_attr
if self.baz.new_record?
raise "baz is new record"
else
raise "baz is not new record"
end
end
end
class Baz
has_many :bars
end
form:
= form_for :foo do |f|
= f.fields_for :bazes do |baz_form|
# form fields for baz
= f.submit
If I raise an error within update_some_attr, and inspect the class, self.baz.new_record? returns true; and yet the ELSE condition fires, which means the Baz model is already persisted before the Bar record is created. I'm just trying to figure out why I'm getting this inconsistency while debugging.
Imgur link to _better_errors console output
Well first of all I have no direct answer to why the console output and the conditional statement both deliver inconsistent behaviour. However I do see opportunity to rewrite the method so the .new_record? condition can be circumvented:
You are applying a before_create callback which means that only on Model.create() the callback is fired so in that case why would one want to condition whether the instance is new? if the method should be used on existing records to update a certain attribute I would just stick to the update_attributes() method. Another method is to always require an exisiting record and change the callback to after_create this will make more sense as your method is called 'update'.
after_create :update_some_attr
def update_some_attr(*args={attribute: :name, value: "John Doe"})
args.assert_valid_keys(:attribute, :value)
attribute, value = [args[:attribute], args[:value]]
self.update_attributes(attribute => value)
end
I think tha you might expect something like this to happen:
Bar belongs_to Foo and to Baz, so before create Bar, ActiveRecord has to create Foo and Baz, get the ids and set it on the associations ids (foo_id and baz_id).
But i believe that the associations are giving you trouble:
Since Foo has many bars and bazs, ActiveRecord will try to save this associations before create Foo and maybe thats why Baz is already persisted on your method.
One solution (if thats the case) is to change the association between Bar and Baz to something like:
class Bar
has_many :bazs
end
class Baz
belongs_to :bar
end
But as i just realized now, you need Baz to have many Bars, so you can try to remove the association on Foo and the accepts_nested_attributes
has_many :basz, through: :bars
acceptes_nested_attributes_for :bazes
And accept the nested attributes on the Bar model:
acceptes_nested_attributes_for :baz
And you will be with something like:
class Foo
has_many :bars
acceptes_nested_attributes_for :bars
end
class Bar
belongs_to :foo
belongs_to :baz
acceptes_nested_attributes_for :baz
before_create :update_some_attr
def update_some_attr
if self.baz.new_record?
raise "baz is new record"
else
raise "baz is not new record"
end
end
end
class Baz
has_many :bars
end
I'm working in Rails 3 and have a table with multiple child tables, e.g.
class Foo < ActiveRecord::Base
has_many :things
has_many :items
has_many :widgets
end
class Thing < ActiveRecord::Base
belongs_to :foo
end
class Item < ActiveRecord::Base
belongs_to :foo
end
class Widget < ActiveRecord::Base
belongs_to :foo
end
Is there a simple way for me to check to if a given Foo has a child record in one or more of the tables? Basically, is there a better way to do this:
if !foo.things.empty? or !foo.items.empty? or !foo.widgets.empty?
puts "This foo is in use!"
emd
Well, I think you're on the right track, but maybe just put that as a method in your Foo model
class Foo < ActiveRecord::Base
def children?
things.any? || items.any? || widgets.any?
end
end
Then just say, Foo.first.children? and get true if the Foo instance has any children.
This is what any? is for.
class Foo < ActiveRecord::Base
def children?
things.any? || items.any? || widgets.any?
end
end
Since this has become a topic of contention, I present to you:
> foo = Foo.last
Foo Load (0.6ms) SELECT "foos"......
> foo.items
Item Load (0.9ms) SELECT "items".*.......
> foo.items.any?
=> true #OH, WHAT's that? NO SQL CALLS? GEE WILLICKERS
> foo.items.exists?
Item Exists (0.5ms) #Hmmmmmm....
=> true
The point here is that under any circumstances, exists makes a DB call, where as any? will not, if spaces is always loaded into memory. Now as I said, many times, the importance is not the efficiency of the DB call (AND YES, the SQL call exists? makes is more efficient), but the fact that any? won't necessarily make a call to the DB, which is a HUGE advantage. Look for yourself:
[20] pry(main)> Benchmark.measure { foo.item.exists? }
Item Exists (0.5ms) SELECT 1 AS one FROM "items" ...
=> #<Benchmark::Tms:0x007fc1f28a8638
#cstime=0.0,
#cutime=0.0,
#label="",
#real=0.002927,
#stime=0.0,
#total=0.00999999999999801,
#utime=0.00999999999999801>
[21] pry(main)> Benchmark.measure { foo.items.any? }
=> #<Benchmark::Tms:0x007fc1f29d1aa0
#cstime=0.0,
#cutime=0.0,
#label="",
#real=7.6e-05,
#stime=0.0,
#total=0.0,
#utime=0.0>
For a more concise timing, look at this:
> Benchmark.measure { 1000.times {foo.items.exists?} }.total
=> 2.5299999999999994
> Benchmark.measure { 1000.times {foo.items.any?} }.total
=> 0.0
Now as I said, many times, it depends on circumstance -- you could have many circumstances where these items aren't loaded into memory, but many times, they are. Choose which one works best for you depending on how you're calling it.
This should work for any given model.
class Foo < ActiveRecord::Base
def children?
has_associated_records = self.class.reflect_on_all_associations.map { |a| self.send(a.name).any? }
has_associated_records.include?(true)
end
end
You could subclass Thing Item and Widget. Or add a polymorphic join table to keep track of it. Not ideal, I know.
You could at least do this, so it would read a little better.
if foo.things.exists? || foo.items.exists? || foo.widgets.exists?
puts "This foo is in use!"
end
'empty?' uses 'exists?' behind the scenes, I believe.
Suppose all the associations are loaded into memory:
class Foo < ActiveRecord::Base
has_many :things
has_many :items
has_many :widgets
def in_use?
[things, items, widgets].flatten.any?
end
end
Edit
I just realized that this is wrong: each association (even if still loaded into memory) will be loaded which isn't good.
things.any? || items.any? || widgets.any?
is more correct and has been proposed before me.
The answer by #Marcelo De Polli is the most generalized one posted so far.
This answer is an updated version of it for Rails 5.
The parent class for a model is ApplicationRecord in Rails 5 and later, which used to be ActiveRecord::Base up to Rails 4 (n.b., the original question is tagged as Rails 3).
For simplicity of the code, use:
class Foo < ApplicationRecord
def children?
self.class.reflect_on_all_associations.map{ |a| self.send(a.name).any? }.any?
end
end
To pursue more run-time efficiency when a model may have many classes of children, use:
class Foo < ApplicationRecord
def children?
self.class.reflect_on_all_associations.each{ |a| return true if self.send(a.name).any? }
false
end
end