My associations are as follows:
Collection:
has_many :children, class_name: "Collection", foreign_key: "parent_id"
belongs_to :parent, class_name: "Collection", foreign_key: "parent_id", optional: true
has_many :albums, dependent: :nullify
Album:
has_many :photos, dependent: :destroy
belongs_to :collection, optional: true
Photo:
belongs_to :album
What I wish to achieve is to, from a collection, pull 9 random photos from ANY Album either directly within the calling Collection or in any sub Collection.
My existing method only seems to pull from albums that are direct children of the calling Collection and that's not quite the effect I am after but unsure how to adjust it.
def photos_for_icon
Photo.joins(:album).where(albums: { collection_id: self.id }).order("RANDOM()").first(9)
end
Anyone have any ideas?
Thanks,
Neil
Related
This is my association set up between my Collection and Album models:
class Collection < ActiveRecord::Base
has_many :children, class_name: "Collection", foreign_key: "parent_id"
belongs_to :parent, class_name: "Collection", foreign_key: "parent_id", optional: true
has_many :albums
end
class Album < ActiveRecord::Base
has_many :photos, dependent: :destroy
belongs_to :collection, optional: true
end
I've just deleted all the Collection's, and I expected the collection_id of each Album to be returned to NULL as the parent no longer exists.
How can I make sure this happens when an Album's parent Collection is deleted?
Use dependent: nullify in your Collection model:
has_many :albums, dependent: :nullify
https://guides.rubyonrails.org/association_basics.html#options-for-has-many
You need to nullify the association foreign_key
I stuck with this. I have a model Position and I need to set an association where a position can be a compounded position (has related positions) and an ingredient position (has parent(s) position) at the same time.
So I created a table related_positions with :copmound_id and :ingredient_id.
To be clear what I need as an output:
related_positions
compound_id | ingredient_id|
pos_1 | pos_2
pos_1 | pos_3
pos_1 | pos_4
pos_5 | pos_2
pos_5 | pos_6
pos_5 | pos_7
pos_1.ingredients = [pos_2, pos_3, pos_4]
pos_5.ingredients = [pos_2, pos_6, pos_7]
pos_2.compounds = [pos_1, pos_5]
It might be kind of self join but with multiple parents
UPDATE:
I found this How to model a many self-referential relationship with many parents?. Which is very close. But I still can't get it work
From the description shared association can be something mentioned below:
class Position
has_many :related_positions, class_name: "RelatedPosition", foreign_key: "ingredient_id", :source => :relate_position
has_many :compounds, class_name: "RelatedPosition", foreign_key: "compound_id", :source => :compound_position
end
class RelatedPosition
belongs_to :relate_position, class_name: "Position"
belongs_to :compound_position, class_name: "Position"
end
Looks like what you want to do is a self-reference
Something like this might work
class Position
has_many :children, class_name: 'Position', foreign_key: 'parent_id'
belongs_to :parent, class_name: 'Position'
end
Thanks to that post http://blog.hasmanythrough.com/2007/10/30/self-referential-has-many-through I've got what I needed.
So, if someone has a similar case here is my solution:
class Position < ApplicationRecord
has_many :parents, class_name: 'RelatedPosition', foreign_key: 'ingredient_id', dependent: :destroy
has_many :children, class_name: 'RelatedPosition', foreign_key: 'compound_id', dependent: :destroy
has_many :compounds, through: :parents
has_many :ingredients, through: :children
end
class RelatedPosition < ApplicationRecord
belongs_to :ingredient, class_name: 'Position'
belongs_to :compound, class_name: 'Position'
end
I have a self-referential association for categories table:
class Category < ApplicationRecord
has_many :subcategories,
class_name: 'Category',
foreign_key: 'parent_id',
dependent: :destroy,
inverse_of: :parent
belongs_to :parent,
class_name: 'Category',
optional: true,
inverse_of: :subcategories
end
I wanted to add act_as_list gem to manage position of elements for main categories and subcategories in scope of main category. As I read documentation it seems that there is no possibility for such scenario. Is there any workaround?
I have a User model:
class User < ActiveRecord::Base
has_many :tracks, dependent: :destroy
has_many :tracked_locations, through: :tracks, source: :tracking, source_type: 'Location'
and a Track model (think of it as 'following'):
class Track < ActiveRecord::Base
belongs_to :user
belongs_to :tracking, polymorphic: true
end
The idea here is I will have many models to track / follow so I am using polymorphism. For example I have a Location model:
class Location < ActiveRecord::Base
has_many :tracks, :as => :tracking, :dependent => :destroy
has_many :users, through: :tracks
Now in the console Location.first.users works fine along with User.first.tracked_locations.
Now I will be adding another polymorphic relationship along the lines of Flagged. The user can 'flag' another model with a note etc. So if I add has_many :users, through: :flagged to the Location model for example I need to differentiate between tracking users and flagged users.
I tried:
has_many :tracking_users, through: :tracks, source: :tracking, source_type: 'User'
but I get:
NoMethodError: undefined method `evaluators_for' for #<Location:0x007ff29e5409c8>
Can I even do this or am I missing something simple here?
UPDATE
Based on the answer below I figured it out:
has_many :tracking_users, through: :tracks, class_name: "User", foreign_key: "user_id", source: :user
I'm not 100% on this, but you could try:
has_many :tracking_users, through: :tracks, class_name: "User", foreign_key: "user_id", source: :user
Or you could also just create a class method and do it by hand.
def self.tracking_users
user_ids = tracks.collect(&:user_id)
User.where(id: user_ids)
end
edit: Had a brainfart, changed the "source" up there to :user. That tells what table to actually do the lookup in with the other attribute you've provided. of course it wouldn't be in :tracks
I have some model, it is self-referential. It contains somethings, which can be child or parent, or both.
Is it possible to do something like this?
class Class < ActiveRecord::Base
belongs_to :parent, class_name: 'Class', foreign_key: :parent_id
has_many :children, class_name: 'Class', foreign_key: :parent_id
has_many :somethings, foreign_key: :something_id
has_many :somethings, through: :children, foreign_key: :something_id
end
What I want to do is call something like parent.somethings and get whole list of them.
For now all I get is stack level too deep error.
Without last line (has_many :somethings, through: :children, foreign_key: :something_id) I can only get child.somethings and it works perfectly.
So basically I want to get list of somethings, that includes somethings of every child of particular parent.
Thanks in advance!
P.S. I need exactly ActiveRecord::Associations::CollectionProxy, so creating method to collect somethings, I think is not possible.
P.P.S. Sorry, if my English is not so good :p
Actually I've found answer myself, after about an hour of research on Active Record Query Interface.
It's pretty simple, so I've done this:
class SomeClass < ActiveRecord::Base
belongs_to :parent, class_name: 'Class', foreign_key: :parent_id
has_many :children, class_name: 'Class', foreign_key: :parent_id
has_many :somethings
def all_somethings(order)
Something.joins(some_class: :parent).where("some_class_id = :id OR parents_some_classes.id = :id", {id: self.id}).uniq.order(order)
end
end
And I get exactly what I wanted!
I'd try splitting it into an instance method that gets all the children's somethings like this:
class Class < ActiveRecord::Base
belongs_to :parent, class_name: 'Class', foreign_key: :parent_id
has_many :children, class_name: 'Class', foreign_key: :parent_id
has_many :somethings, foreign_key: :something_id
def all_somethings
return unless self.children.empty?
(self.children + self.children.map(&:all_somethings)).uniq
end
end
I am writing an updated answer of Jonathan to avoid multiple call to :all_somethings and making query to get all the results. I hope this will also give you the results.
class Class < ActiveRecord::Base
belongs_to :parent, class_name: 'Class', foreign_key: :parent_id
has_many :children, class_name: 'Class', foreign_key: :parent_id
has_many :somethings, foreign_key: :something_id
has_many :childrens_somethings, through: :children, foreign_key: :something_id,
class_name: "Something"
def all_somethings
return unless self.children.empty?
(self.children + self.children_somethings).uniq
end
end