ruby-on-rails Rails:通过多对多关系模型嵌套的项目列表

4xy9mtcn  于 2023-03-09  发布在  Ruby
关注(0)|答案(1)|浏览(152)

我是Rails的新手,有些地方我不太理解。
我有4个模型如下:

class Unit
  belongs_to :compound
  belongs_to :unit_type
end
class UnitType
  has_many :units
  has_many :unit_type_compounds
  has_many :compounds, through: :unit_type_compounds
end
class Compound
  has_many :units
  has_many :unit_type_compounds
  has_many :unit_types, through: :unit_type_compounds
end
class UnitTypeCompound
  belongs_to :unit_type
  belongs_to :compound
end

我希望通过unit_type_compound获取所有units [具有活动状态]
我尝试了以下方法,但是当我检查查询时,我发现它是错误的
第一:

class UnitTypeCompound
  belongs_to :unit_type
  belongs_to :compound

  has_many :units, through: :unit_type
end

查询[针对单个项目UnitTypeCompound.all.first.units ]类似于:

SELECT `units`.*
FROM `units`
  INNER JOIN `unit_types` ON `units`.`unit_type_id` = `unit_types`.`id`
WHERE `unit_types`.`id` = ?
  /* loading for inspect */
LIMIT 11

第二:

class UnitTypeCompound
  belongs_to :unit_type
  belongs_to :compound
  has_many :units, through: :unit_type, source: :unit_type_compounds

  has_many :vacant_units, ->{ where(status: :vacant) }, class_name: Unit.to_s,
           through: :unit_type, source: :unit_type_compounds
end

查询[针对单个项目UnitTypeCompound.all.first.units ]类似于:

SELECT `units`.*
FROM `units`
  /* Note: the following condition is units.id, not compounds.id */
  INNER JOIN `unit_type_compounds` ON `units`.`id` = `unit_type_compounds`.`compound_id`
  INNER JOIN `unit_types` ON `unit_type_compounds`.`unit_type_id` = `unit_types`.`id`
WHERE `unit_types`.`id` = ?
  /* loading for inspect */
LIMIT 11

查询[对于单个项目UnitTypeCompound.all.first.vacant_units ]如下所示:

SELECT `units`.*
FROM `units`
  /* Note: the same condition */
  INNER JOIN `unit_type_compounds` ON `units`.`id` = `unit_type_compounds`.`compound_id`
  INNER JOIN `unit_types` ON `unit_type_compounds`.`unit_type_id` = `unit_types`.`id`
WHERE `unit_types`.`id` = ?
  AND `units`.`status` = 0
  /* loading for inspect */
LIMIT 11

第三:
它使用的方法是

class UnitTypeCompound
  belongs_to :unit_type
  belongs_to :compound

  def vacant_units
    Unit.where(status: :vacant)
        .where(compound: compound)
        .where(unit_type: unit_type)
  end
end

查询[对于单个项目UnitTypeCompound.all.first.vacant_units ]如下所示:

SELECT `units`.*
FROM `units`
WHERE `units`.`sellable` = FALSE
    AND `units`.`compound_id` = ?
    AND `units`.`unit_type_id` = ?
    AND `units`.`status` = 0
    /* loading for inspect */
LIMIT 11

但是,我想知道是否有一种方法通过has_many或其他东西?

hjzp0vay

hjzp0vay1#

您的场景不适用于has_many,原因如下:UnitTypeCompound通过两个不同的关联拥有许多units,而您需要这两个关联的units的交集。
第一个不适用于has_many的原因是,如果使用自然语言,您会认为如果UnitTypeCompound有许多单位,它将是这两个关联的并集,而不是交集。
第二个原因是has_many应该是可逆的,如果你调用unit.unit_type_compounds,你会期望它是unit.unit_type.unit_type_compoundsunit.compound.unit_type_compounds,这两者的并集还是交集?
第三,你应该能够在关联上调用collection<<方法,如果你调用了unit_type_compound.units << Unit.last,它应该通过UnitType还是Compound创建那个关联?
您的场景有很多不确定性,无法用简单的has_many :units, through:来表示,因此如果vacant_units方法对您有效,我将坚持使用它。
如果你想尝试用一个关联来做它,我最好的猜测是它应该看起来像这样:

class UnitTypeCompound
  belongs_to :unit_type
  belongs_to :compound

  has_many :vacant_units, ->(unit_type_compound){ where(compound_id: unit_type_compound.compound_id, status: :vacant) },
           through: :unit_type, source: :units 
end

相关问题