0

Attempting to create a join table that takes two attributes on one model, and associates them with the id of a second model. Cannot use the id of the first model due to a constant refresh process, that reorders the rows on that model's table (ConstantlyRefreshingModels).

class ConstantlyRefreshingModels < ApplicationRecord
  attr_accessor :home_id, :chair, :floor, :number 
  has_and_belongs_to_many :family
end 

class Family < ApplicationRecord
  attr_accessor :bread_winner
  has_and_belongs_to_many :constantlyrefreshingmodel
end

class CreateFamilyModelThatConstantlyRefreshesJoinTable < ActiveRecord::Migration[5.0]
  def change
    create_join_table :families, :constantlyrefreshingmodel
  end
end

Looking for a join table that looks like this:

home_id | number | family_id
-----------------------------
    2   | '3003' |    4
    1   | '2100' |    1

And then this works out for exposing data:

new_home = constantlyrefreshingmodel.where(home_id: 2)
new_home.families == 4 == true

Any suggestions as to creating the join table, and the declaring of the associations?

JoaWa
  • 35
  • 1
  • 9
  • What version of Rails are you using? – s3tjan Mar 16 '19 at 03:53
  • "Cannot use the id of the first model due to a constant refresh process, that reorders the rows on that model's table" - well how are you supposed to link two tables together if there is not any kind of stable identifier? – max Mar 16 '19 at 10:24
  • This not even a Rails problem. Its a database design problem. To link table B to table A - B must be able to reference A. If for whatever reason you can't use the default auto-incrementing primary keys then you need to use UUIDs or some other type of column that can be used for foreign keys instead. – max Mar 16 '19 at 10:27
  • @s3tjan Rails 5.1 – JoaWa Mar 18 '19 at 14:12
  • @max I agree, it would be ideal if the ConstantlyRefreshingModels primary id's were reliable but with the technology this is representing in the real world we use the `:home_id` and `:number` combination to know how to update our records, due to their possessing more meaning to our customers and developers. – JoaWa Mar 18 '19 at 14:16
  • Let me get this strait, it seems to me that you are trying to know what home is assigned to a family. This "assignation" changes very often so a refreshing model was implemented. I am correct? – s3tjan Mar 18 '19 at 15:22
  • @s3tjan Sort of, in this particular example I need to be able to expose that a home_id of 2 and the number 3003 points to the family where the id is four, and vice versa. Family with an id of 4 is associated to the home with an id of 2 and the number 3003. There is the possibility of a single home_id, with two different numbers, have two different family_ids associated. – JoaWa Mar 18 '19 at 16:46

1 Answers1

1

You need a has_many :through Association not a has_and_belongs_to_many since you have additional attributes (:number) in join model

You should use has_many :through if you need validations, callbacks or extra attributes on the join model. Section 2.8

Try this approach:

class Home < ApplicationRecord
  has_many :points
  has_many :families, through: :points
end

# Point is your join model, AKA ConstantlyRefreshingModel
class Point < ApplicationRecord
  belongs_to :family
  belongs_to :home

  validates :points, presence: true # Or some other validation, :number in your question.
end

class Family < ApplicationRecord
  has_many :points
  has_many :houses, through: :points
end

Join table migration:

class CreatePoints < ActiveRecord::Migration[5.2]
  def change
    create_table :points do |t|
      t.references :home,   foreign_key: true, null: false
      t.references :family, foreign_key: true, null: false
      t.string     :points, null: false

      t.timestamps
    end
  end
end

For example, having the following data:

home_1 = Home.create(id: 1)  # home with an id of 1
home_2 = Home.create(id: 2)  # home with an id of 2

family_4 = Family.create(id: 4) # family with an id of 4
family_1 = Family.create(id: 1) # family with an id of 1

Point.create(points: "3003", home: home_2, family: family_4)
Point.create(points: "2100", home: home_1, family: family_1)

Then:

points = Point.where(home: home_2)  # you get a collection of all points assigned to house with ID: 2
points.first.family                 # family with an id of 4

To expose that a :home with id: 2 and the number 3003 points to the family where the id: 4

home = Home.find(2)
home.families.ids # 4
home.points.first # 3003

To expose family with an id: 4 is associated to the home with an id of 2 and the number 3003.

family = Family.find(4)
family.homes.ids     # 2
family.points.first  # 3003

Also, attr_accessor, attr_reader and attr_writer are deprecated in Rails 5.1. See this post

s3tjan
  • 1,118
  • 10
  • 13
  • Thanks! Ended up doing a joining model, and using the has many :through as you recommended. Good point on the attr configs. – JoaWa Mar 19 '19 at 17:13