Mr. Tao
Mr. Tao

Reputation: 863

Self-referencing Rails HABTM association works one way only

I have documents and some of them are related and the relation is symmetric. That said, I tried to model this relation according to HABTM instructions for both association and join table.

I can do d1.related_documents << d2 for two instances of Document. d1.related_documents works and returns related documents, but d2.related_documents returns an empty set.

I have declared tables and model as follows:

Upvotes: 1

Views: 353

Answers (1)

stwienert
stwienert

Reputation: 3632

A self referential habtm is a little outside the standard Rails toolbox. You need to adjust the finder_sql or have 2 relations:

has_and_belongs_to_many :related_documents, join_table: :related_documents, 
                                            class_name: "Document", 
                                            foreign_key: :referent_id, 
                                            association_foreign_key: :reference_id
has_and_belongs_to_many :referenced_documents, join_table: :related_documents, 
                                            class_name: "Document", 
                                            foreign_key: :reference_id, 
                                            association_foreign_key: :referent_id

If you need to unionize the results into one SQL, you need to adjust the Finder-SQL, see e.g. https://gist.github.com/srpouyet/4121517

has_and_belongs_to_many :related_documents,
                          class_name: 'Document',
                          join_table: :related_documents,
                          foreign_key: :reference_id,
                          association_foreign_key: :referent_id,
                          uniq: true,
                          finder_sql: proc { 
     %(SELECT DISTINCT "documents".* FROM "documents"
       INNER JOIN "related_documents" 
         ON "documents"."id" = "related_documents"."referent_id"
       WHERE "related_documents"."reference_id" =  #{id}
       UNION
       SELECT DISTINCT "documents".* FROM "documents"
       INNER JOIN "related_documents" 
         ON "documents"."id" = "related_documents"."reference_id"
       WHERE "related_documents"."referent_id" =  #{id} 
       )}

Upvotes: 1

Related Questions