英文:
Rails: List of a nested items through a many-to-many relation model
问题
以下是您要翻译的内容:
"我是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` = ?
/* 用于检查加载 */
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`
/* 注意:以下条件是units.id,而不是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` = ?
/* 用于检查加载 */
LIMIT 11
以及查询(针对单个项目UnitTypeCompound.all.first.vacant_units
)如下所示:
SELECT `units`.*
FROM `units`
/* 注意:相同的条件 */
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
/* 用于检查加载 */
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
/* 用于检查加载 */
LIMIT 11
不过,我想知道是否有一种通过has_many
或其他方式来实现的方法?"
英文:
I am new to rails and there's something I don't fully understand.
I have 4 models as follows:
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
I want to get all units
[with active status] through a unit_type_compound
I've tried the following but when I inspect the query I find it's wrong
first:
class UnitTypeCompound
belongs_to :unit_type
belongs_to :compound
has_many :units, through: :unit_type
end
the query [for a single item UnitTypeCompound.all.first.units
] was something like:
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
second:
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
the query [for a single item UnitTypeCompound.all.first.units
] was something like:
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
and query [for a single item UnitTypeCompound.all.first.vacant_units
] was something like:
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
third:
It worked using the method
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
and query [for a single item UnitTypeCompound.all.first.vacant_units
] was something like:
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
but, I wonder if there's a way through has_many
or something?
答案1
得分: 1
你的情景不适合使用has_many
,有几个原因。您处于一种情况,UnitTypeCompound
通过两个不同的关联关系具有许多units
,并且您希望从这两个关联关系中获取units
的交集。
第一个原因,这不应该与has_many
一起使用,因为使用自然语言,您会期望如果UnitTypeCompound
有许多单位,它将是这两个关联的并集,而不是交集。
第二个原因是,has_many
应该是可逆的。如果您调用unit.unit_type_compounds
,您会期望它是unit.unit_type.unit_type_compounds
,unit.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
英文:
Your scenario is not suitable for has_many
for a few reasons. You are in a situation where UnitTypeCompound
has many units
through two different associations and you want an intersection of units
from these two associations.
First reason this should not work with has_many
is that using natural language you would expect that if UnitTypeCompound
has many units it would be a union of those two associations, not an intersection.
Second reason is that has_many
should be reversible. If you call unit.unit_type_compounds
would you expect it to be unit.unit_type.unit_type_compounds
, unit.compound.unit_type_compounds
, union of those two or their intersection?
Third is that you should be able to call collection<<
method on the association. If you called unit_type_compound.units << Unit.last
should it create that association through UnitType
or Compound
?
Your scenario has a lot of abibiguity that cannot be expressed by a simple has_many :units, through:
therefore if the vacant_units
method works for you, I would stick with it.
If you want to try to do it with an association, my best guess is that it should look like this:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论