Home > OS >  Rails bi-directional / self join model
Rails bi-directional / self join model

Time:09-11

Problem: I have a model Exercise, this model can have exercises that are variations of each other. So a self join table I thought.

Like a join table of Foo, Boo, Coo, where Foo and Boo are models link by the joining table Coo allows for @foo.boo and @boo.foo

So I looked and read these posts describing bi and uni directional relation ships 1, 2. So the table stores the data correctly but now I'm have trouble on creating the correct query.

Using the methods listed above, saving a record => Foo.variation_exercises = Boo Which then allows @foo.exercises = boo but if I do @boo.exercises it returns an empty collection, instead of foo. In order to get foo it would be @boo.variations.

What are the steps I need to take to make @foo.exercises and @boo.exercises work? Is it something to do with the foreign_key or migration file?

Schema migration, Models, Controllers, Forms:

create_table :exercise_variation_relations do |t|
  t.references :exercise, foreign_key: true, null: false, index: true
  t.references :exercise_variation, foreign_key: { to_table: :exercises }, null: false, index: true 
end

class ExerciseVariationRelation < ApplicationRecord
        belongs_to :exercise, foreign_key: "exercise_id", class_name: "Exercise" 
        belongs_to :exercise_variation, foreign_key: "exercise_variation_id", class_name: "Exercise" 
    end

class Exercise < ApplicationRecord
**other code
        has_many :exercise_drills, foreign_key: :exercise_variation_id, class_name: "ExerciseVariationRelation"
        has_many :exercises, through: :exercise_drills, source: :exercise
    
     
has_many :variation_exercises, foreign_key: :exercise_id, class_name: "ExerciseVariationRelation"
has_many :variations, through: :variation_exercises, source: :exercise_variation

    validates_associated: :variation_exercises
    accepts_nested_attributes_for :variation_exercises

    ** other code
    end

Contoller 
params.fetch(:exercise, {}).permit( **other params
                                   variation_exercises_attributes: [:id, :exercise_variation_id, :_destroy], )

Form
   <%= f.fields_for :variation_exercises, ExerciseVariationRelation.new, child_index: 'NEW_RECORD' do |e| %>
    <%= render "form_variation", form: e %>
   <% end %>

Form_variation
<%= content_tag :div, class: "nested-fields" do %>
<%= form.collection_select(:exercise_variation_id, Exercise.all, :id, :name, {}, {class: 'form-control'}) %>
<% end %>

I understand how join tables work but this self join/relation is confusing still.

CodePudding user response:

ActiveModel::UnknownAttributeError (unknown attribute 'exercise_variation_id' for Exercise.)

The error message is clearly Exercise doesn't have exercise_variation_id.

Could you show your app/db/schema.rb file content? Make sure your migration creates exercise_variation_id for Exercise in the database.

CodePudding user response:

i think your model (at least how it is presented in your question) needs some clarification :) for instance:

  • do these relations act like a transitive network? if A is a variation of B and B is a variation of C, does that make A a variation of C?
  • if A is a variation of B, does that make B a variation of A?

in the simplest case, this should work i think:

create_table :exercise_variations do |t|
    t.references :exercise, foreign_key: true, null: false, index: true
    t.references :variation, foreign_key: { to_table: :exercises }, null: false, index: true 
end

class ExerciseVariation < ApplicationRecord
    belongs_to :exercise, foreign_key: "exercise_id"
    belongs_to :variation, foreign_key: "variation_id", class_name: "Exercise" 
end

class Exercise < ApplicationRecord
    has_many :exercise_variations, class_name: "ExerciseVariation"
    has_many :variations, through: :exercise_variations
    
    has_many :variation_exercises, foreign_key: :variation_id, class_name: "ExerciseVariation"
    has_many :variations, through: :variation_exercises
end
  • Related