问题
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