Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
387 views
in Technique[技术] by (71.8m points)

ruby on rails - Table name in has_many through relation

I have a model with a couple through associations looking for siblings, i.e. records that have the same parent but are not itself.

class Person
  has_many :siblings, -> { where("being_siblings.id != beings.id") }, through: :beings, source: :self_and_related_beings
  has_many :nice_humans, through: :siblings, source: :human
end

My issue is that if I run person.nice_humans the join table used by the query is named differently (in this case something like being_nice_humans) than when I run person.siblings in which case it is called something like being_siblings. Is there any way that I can tell rails what to alias the table as or otherwise make this work?

I have tried something like -> (p) { where.not("beings.id = ?", p.id) } but I need to use this in an includes which is not allowed when I pass the instance in.

Note: My example is based on a real application but I have the names of the associations.

Any help is much appreciated!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You can't actually define an assocation like that since the definition of an assocation is class and not instance level.

While you can use a lambda to add default scoping to an assocation that lambda does not take arguments as there is no way to pass those arguments when joining, including or eager loading an association. self inside the lambda is an association proxy and not the record itself so you can't write ->{ where.not(id: self.id) }.

What you can do is write an instance method:

class Person < ApplicationRecord
  belongs_to :parent, optional: :true, 
                      class_name: 'Person'

  has_many :siblings, class_name: 'Person',
                      foreign_key: :parent_id,
                      primary_key: :parent_id
  
  def siblings
    # super is the getter created by the association
    super.where.not(id: self.id)
  end
end

Example usage:

irb(main):001:0> Person.all
  Person Load (0.2ms)  SELECT "people".* FROM "people" LIMIT ?  [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Person id: 1, name: "Odin", parent_id: nil, created_at: "2021-01-05 20:36:23", updated_at: "2021-01-05 20:36:23">, #<Person id: 2, name: "Thor", parent_id: 1, created_at: "2021-01-05 20:37:51", updated_at: "2021-01-05 20:37:51">, #<Person id: 3, name: "Balder", parent_id: 1, created_at: "2021-01-05 20:38:56", updated_at: "2021-01-05 20:38:56">]>
irb(main):002:0> Person.last.siblings
  Person Load (0.2ms)  SELECT "people".* FROM "people" ORDER BY "people"."id" DESC LIMIT ?  [["LIMIT", 1]]
  Person Load (0.2ms)  SELECT "people".* FROM "people" WHERE "people"."parent_id" = ? AND "people"."id" != ? LIMIT ?  [["parent_id", 1], ["id", 3], ["LIMIT", 11]]
=> #<ActiveRecord::AssociationRelation [#<Person id: 2, name: "Thor", parent_id: 1, created_at: "2021-01-05 20:37:51", updated_at: "2021-01-05 20:37:51">]>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...