Act as list with self-referential association - ruby-on-rails

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?

Related

self join how to reference the association from parent

I have Category class shown below
class Category < ActiveRecord::Base
has_many :subcategories, class_name: "Category", foreign_key: "parent_category_id"
belongs_to :parent_category, class_name: "Category"
belongs_to :main_category
end
and I wonder if I can define main_category association the rails way that I can
reference the #main_category on subcategories but leaving the main_category_id empty (as the reference on subcategory#main_category_id will duplicate the data which is in parent_category#main_category_id or it is just premature optimization?😅 ).
category = Category.new main_category: main_category
subcategory = Category.new parent_category: category
assert_equal category.main_category, subcategory.main_category
you can create a proxy that subcategories will delegate main_category to parent, the drawback that there're 3 queries to get main_category
class Category < ActiveRecord::Base
has_many :subcategories, class_name: "Category", foreign_key: "parent_category_id"
belongs_to :parent_category, class_name: "Category"
belongs_to :main_category, class_name: "Category"
# with subcategories, there're 3 queries:
# super return nil -> find parent_category -> find main_category of the parent
def main_category
super || parent_category&.main_category
end
end
You can use indirect assocations to setup "short-cuts" through the tree.
class Category < ActiveRecord::Base
# Going up...
has_many :subcategories,
class_name: "Category",
foreign_key: "parent_category_id"
has_many :grand_child_categories,
class_name: "Category",
through: :subcategories
has_many :great_grand_child_categories,
class_name: "Category",
through: :grand_child_categories
# Going down...
belongs_to :parent_category,
class_name: "Category"
has_one :grand_parent_category,
through: :parent_category,
class_name: "Category"
has_one :great_grand_parent_category,
through: :grand_parent_category,
class_name: "Category"
end
However if you have a heirarchy of unlimited depth ActiveRecord::Assocations can't really solve the problem of finding the node at the bottom of the tree. That requires more advanced SQL like a recursive common table expression (CTE).
While ActiveRecord does has the basic tools for creating self joining assocations most of the more andvaced stuff is out of scope and can be handled with gems like Ancestry.

Rails Set child association id to nil when deleting parent

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

Rails model has_many association to the same model

I have two models: Category and Subcategory
And I can have situation that Subcategory will include more Subcategories
How Can I do it with rails associations?
Now my code:
category.rb
class Category < ApplicationRecord
has_many :subcategories, :dependent => :destroy
end
subcategory.rb
class Subcategory < ApplicationRecord
belongs_to :category
has_many :products, :dependent => :destroy
end
Possible example:
Category Readable --> Subcategory Books --> Subcategory Books for kids --> products
This is a good case for a polymorphic belongs_to association.
#on Subcategory model
belongs_to :parent, polymorphic: true
has_many :subcategories, as: :parent, dependent: :destroy
#on Category model
has_many :subcategories, as: :parent, dependent: :destroy
#on the database
t.references :parent, polymorphic: true, index: true # this adds two columns, parent_id and parent_type
Now you can assign anything anything as the parent for a subcategory and you can call subcategory.parent to get either a Category or a Subcategory
https://guides.rubyonrails.org/association_basics.html#polymorphic-associations
You can try to add subcategory_id to your subcategory model by migration and add has_many :subcategories in your subcategory model.
Or you can add
belongs_to :parent, :class_name => "Subcategory", :foreign_key => "parent_subcategory_id"
has_many :child_subcategories, :class_name => "Subcategory", :foreign_key => "child_subcategory_id"

Get products of children categories

I have a model, called Groups, it's self-referential, there are groups in it - parents and children together, linked by parent_id. So I can get children of a group and products connected to one of groups, but how can I get all products of parent group through its children?
Here is my model:
class Group < ActiveRecord::Base
belongs_to :parent, class_name: 'Group', foreign_key: :parent_id
has_many :children, class_name: 'Group', foreign_key: :parent_id
has_many :products
validates :id, uniqueness: true
self.primary_key = :id
end
It would be great if, for example, I could call group.products and get all products, that are direct descendants of that group and all products of children groups.
It would be even better if I could paginate them with something like Kaminari(or another paginator).
P.S. I tried to insert someting like has_many :products, through: :children, but all I get is Stack level is too deep...
P.P.S. I can create method that will collect all products in Hash, but I won't be able to sort list of products by title, price etc.
Try this
class Group < ActiveRecord::Base
belongs_to :parent, class_name: 'Group', foreign_key: :parent_id
has_many :children, class_name: 'Group', foreign_key: :parent_id
has_many :products
has_many :child_products, :through => :children, :source => :products
validates :id, uniqueness: true
self.primary_key = :id
end

Rails scope checking for no associations

I have a Category model where a category may have some subcategories (category.categories). I want a scope that gives me all Categorys that have no subcategories.
In other words, I can write
without_subcategories = Category.select{|category| category.categories.none?}
but I would like to write this as a scope. How do I do this?
In case it's not clear, this is my model:
class Category < ActiveRecord::Base
belongs_to :parent, class_name: 'Category'
has_many :categories, foreign_key: :parent_id, class_name: 'Category'
scope :without_subcategories, lambda { WHAT GOES IN HERE? }
end
the best practice is minimize db queries by implementing a counter cache.
In rails this is super simple by adding an option :counter_cache => true to the belongs_to association. This assumes you create a 'categories_count' integer column in your categories db table. Given this, your scope is trivial.
class Category < ActiveRecord::Base
belongs_to :parent, class_name: 'Category', :counter_cache => true
has_many :categories, foreign_key: :parent_id, class_name: 'Category'
scope :without_subcategories, where(categories_count: 0)
end
hope this helped.

Resources