Symmetrical, self-referential HABTM relationship

蹲街弑〆低调 提交于 2019-12-31 04:05:11

问题



In my schema I have a model called Imprintables. In this model I have this self-referential relationship:

class Imprintable < ActiveRecord::Base
  has_and_belongs_to_many :coordinates, class_name: 'Imprintable', association_foreign_key: 'coordinate_id', join_table: 'coordinates_imprintables'
...
end

In the imprintable create/edit form I have a select field where users can select coordinate imprintables (coordinates are used to identify imprintables that are similar to the one being created/edited). My issue is that if I have an imprintable A and and imprintable B, when I create A and list B as a coordinate imprintable, I want to be able to look at the edit form for B and see A also listed as a coordinate.

I've tried modifying the controller's update and create action to insert both pairs of id's into the linker table (coordinates_imprintables). For instance if A.id = 1 and B.id = 2, then executing some sql like:

INSERT INTO coordinates_imprintables (imprintable_id, coordinate_id) VALUES (1, 2)
INSERT INTO coordinates_imprintables (imprintable_id, coordinate_id) VALUES (2, 1)

But I couldn't get it working because I'm not sure how to know when a user is trying to delete or insert a coordinate, and I'm not even sure if that's the correct answer anyway. Railscasts have been marginally helpful, this just seems like a tricky case because I have a model with a self-referential relationship that is also symmetrical. Thanks for you time!


回答1:


What you seem to be looking for here are model callbacks. These execute before, after, or during actions in an object's lifecycle.

In order to use these for your situation, you can add a model for your CoordinatedImprintable table. Then, whenever you create, update, or destroy a coordinate_imprintable, you'll need to do the same thing for its mirror.

class CoordinateImprintable < ActiveRecord::Base
  belongs_to :imprintable
  belongs_to :coordinate, class_name: 'Imprintable', foreign_key 'imprintable_id'
  after_create :add_mirror
  after_update :update_mirror
  after_destroy :destroy_mirror

  def add_mirror
    self.class.find_or_create(imprintable: coordinate, coordinate: imprintable)
  end

  def update_mirror
    if self.changed?
      mirror = self.class.find(imprintable: coordinate_was, coordinate: imprintable_was)
      mirror.update_attributes(imprintable: coordinate, coordinate: imprintable)
    end
  end

  def destroy_mirror
    mirror = self.class.find(imprintable: coordinate, coordinate: imprintable)
    mirror.destroy if mirror && !mirror.destroyed
  end
end

You'll also need change your has_and_belongs_to_many relation to two has_many :through relations.

class Imprintable
  has_many :coordinate_imprintables
  has_many :coordinate, through: :coordinate_imprintables
  has_many :mirrored_coordinate_imprintables, class_name: 'CoordinateImprintable', foreign_key: 'coordinate_id'
  has_many :mirrored_coordinates, through: :mirrored_coordinate_imprintables, source: :imprintable
end

Hopefully this helps! And feel free to comment with any questions.



来源:https://stackoverflow.com/questions/24310533/symmetrical-self-referential-habtm-relationship

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!