rails model has_many of itself
Asked Answered
S

6

40

I have a event model. Events can have parent events, set from a column in the model (parent_event_id). I need to be able to do has_many :event on the model, so I can just do, for example, event.child_event or event.parent_event. But my googling hasn't worked out that well.

My Model:

class Event < ActiveRecord::Base
    attr_accessible :days_before, :event_name, :event_date, :list_id, :recipient_email, :recipient_name, :parent_event_id, :is_deleted, :user_id

    belongs_to :user
    has_many :event_email
    has_many :event
end

My Schema:

create_table "events", :force => true do |t|
    t.datetime "event_date"
    t.integer  "days_before"
    t.string   "recipient_email"
    t.integer  "list_id"
    t.string   "recipient_name"
    t.datetime "created_at",                         :null => false
    t.datetime "updated_at",                         :null => false
    t.integer  "user_id"
    t.string   "event_name"
    t.integer  "parent_event_id"
    t.boolean  "is_deleted",      :default => false
end
Seeress answered 13/9, 2013 at 17:20 Comment(0)
B
75

This is a self-referential model, you can try something like this:

class Event < ActiveRecord::Base
  belongs_to :parent, :class_name => "Event", :foreign_key => "parent_event_id"
  has_many :child_events, :class_name => "Event", :foreign_key => "child_event_id"
end

That way, you can call @event.parent to get an ActiveRecord Event object and @event.child_events to get an ActiveRecord collection of Event objects

Blum answered 13/9, 2013 at 17:33 Comment(3)
do we need foreign_key in both belongs_to and has_many ?Daleth
Can we use accepts_nested_attributes_for :child_events, allow_destroy: true?Mcgarry
This solution sets the parent id to the "child_event_id" field. It works as expected if I use "parent_event_id" as the foreign key for both relationshipsRock
I
4

You will want to change your has_many to something like this:

has_many :parent_events, class_name: 'Event'
has_many :child_events, ->(event) { where parent_event_id: event.id }, class_name: 'Event'

This is from the rails 4 docs at the link: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Specifically, the section on "customizing the query". This should allow you to do what you're looking for. Didn't try it locally, but this is similar to what I had to do to implement a football pickem app I did a while back.

Hope this helps.

Izabel answered 13/9, 2013 at 17:35 Comment(0)
C
2

Try Nested Set Pattern

For this one: Awesome Nested Set

Casie answered 13/9, 2013 at 17:25 Comment(0)
V
2

Rails already has a gem for providing nested tree structure ancestry. It will be best in such scenarios:

https://github.com/stefankroes/ancestry

You will be able to access following methods:

event.parent
event.children
event.siblings
Vallery answered 13/9, 2013 at 17:29 Comment(0)
N
1

I found that in Rails 5 the belongs to has become mandatory by default so couldn't save my model instances... adding optional to the first line of the recommended solution fixes this...

class Event < ActiveRecord::Base
  belongs_to :parent, :class_name => "Event", :foreign_key => "parent_event_id", optional: true
  has_many :child_events, :class_name => "Event", :foreign_key => "parent_event_id"
end
Noose answered 18/11, 2017 at 21:44 Comment(0)
D
0

I'm not sure that this is new for Rails 6, but the Rails Guide on Active Record Associations has a section on self-joins that provides a neater solution:

class Event < ApplicationRecord
  has_many :children, class_name: "Event", foreign_key: "parent_id"
 
  belongs_to :parent, class_name: "Event", optional: true
end

Note here that only the child events need the parent_id. The optional is necessary in the belongs_to association to allow you to save your models, as belongs_to is mandatory as of Rails 5.

Discomfort answered 20/8, 2020 at 14:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.