我也认为 has_many through 关系更适合:has_and_belongs_to_many vs has_many through
这是一个不需要在 ruby 中过滤结果的选项:
- 选择其中包含每个所需人员的所有
FamilyPerson 对象
- 选择所有
FamilyPerson 对象中的人不是所需的人
- 在第一个查询结果而不是第二个查询结果中选择所有具有 ID 的
Family 对象 - 换句话说,那些拥有所有必要人员但没有更多人的家庭
实施
app/models/family.rb
class Family < ActiveRecord::Base
has_many :family_people
has_many :people, through: :family_people
def self.all_with_members members
member_ids = members.collect {|m| m.id }
# FamilyPersons that have each of the members in it
enough = FamilyPerson.where(person_id: member_ids).group("family_id").having("count(person_id) >= ?", member_ids.count).collect { |fp| fp.family_id }
# FamilyPersons that have no additional members
too_many = FamilyPerson.where("person_id NOT IN(?)", member_ids).group("family_id").having("count(person_id) > 0").collect { |fp| fp.family_id }
Family.find enough - too_many
end
end
app/models/person.rb
class Person < ActiveRecord::Base
has_many :family_people
has_many :families, through: :family_people
end
app/models/family_person.rb
class FamilyPerson < ActiveRecord::Base
belongs_to :family
belongs_to :person
end
摘自 db/schema.rb
create_table "families", force: true do |t|
t.string "name"
end
create_table "family_people", force: true do |t|
t.integer "person_id"
t.integer "family_id"
end
create_table "people", force: true do |t|
t.string "name"
end
测试
spec/models/family_spec.rb
使用 RSpec 进行测试
require 'rails_helper'
describe Family do
before :each do
Rails.application.load_seed
end
describe "self.all_with_members" do
it "returns all families that have each person in it" do
people = [Person.find_by_name("Joe"), Person.find_by_name("Jane"), Person.find_by_name("Ben")]
expect(Family.all_with_members(people).to_a).to eq Family.where(name: "Family 1").to_a
end
end
end
db/seeds.rb
种子数据
Family.destroy_all
Person.destroy_all
FamilyPerson.destroy_all
joe = Person.create name: "Joe"
jane = Person.create name: "Jane"
ben = Person.create name: "Ben"
mary = Person.create name: "Mary"
family_one = Family.create name: "Family 1"
family_two = Family.create name: "Family 2"
family_three = Family.create name: "Family 3"
family_one.people << joe
family_one.people << jane
family_one.people << ben
family_one.save!
family_two.people << jane
family_two.people << ben
family_two.people << mary
family_two.save!
family_three.people << joe
family_three.people << jane
family_three.people << ben
family_three.people << mary
family_three.save!