我有一个模型Place
。
例如,地点可以是城市、地区、地区或国家,但我认为一种模型就足够了,因为对于我的目的来说,城市和地区之间没有太大差异。
并且地点可以彼此属于,例如一个地区可以有很多城市,一个国家可以有很多地区等等。
但我的问题是,这是一种历史工程,一个城市可以属于多个地区。例如,从 1800 年到 1900 年,一座城市属于一个历史区域(现在不存在),从 1900 年至今,一座城市属于另一个历史区域。将这些区域存储为不同的地方很重要,尽管它们可能具有相似的地理边界,但只有名称不同。
我猜这是多对多,但也许有人可以给出更好的主意?
如果是多对多,我如何在一个查询中获取一系列父地点,以生成简单的字符串,例如“地点,父地点 1,父地点 2,父地点 3”,例如“美国加利福尼亚州旧金山”?
这是我的代码:
create_table :places do |t|
t.string :name
t.timestamps null: false
end
create_table :place_relations, id: false do |t|
t.integer :sub_place_id
t.integer :parent_place_id
t.timestamps null: false
end
class Place < ActiveRecord::Base
has_and_belongs_to_many :parent_places,
class_name: "Place",
join_table: "place_relations",
association_foreign_key: "parent_place_id"
has_and_belongs_to_many :sub_places,
class_name: "Place",
join_table: "place_relations",
association_foreign_key: 'sub_place_id'
end
请随时给我一些想法!
最佳答案
这是我想到的第一个解决方案,可能还有很多其他方法可以实现,但我相信这可能是最干净的。
您的总体思路是正确的,但您所需要的只是对连接表进行轻微修改。本质上,您将使用 has_many... through
关系来代替,以便您可以附加某种时间范围鉴别器。
在我的示例中,我使用日期时间字段来指示关联从哪个点开始相关。结合按时间鉴别器对结果进行排序的默认范围(在我的示例中称为 effective_from
),您可以轻松选择某个地点的“当前” parent 和 child ,而无需额外的努力,或者选择历史在 where 子句中使用单个日期比较的数据。 请注意,您不需要像我一样处理时间范围歧视,这只是为了演示这个概念。根据需要进行修改。
class PlaceRelation < ActiveRecord::Base
default_scope { order "effective_from DESC" }
belongs_to :parent, class_name: "Place"
belongs_to :child, class_name: "Place"
end
class Place < ActiveRecord::Base
has_many :parent_places, class_name: "PlaceRelation", foreign_key: "child_id"
has_many :child_places, class_name: "PlaceRelation", foreign_key: "parent_id"
has_many :parents, through: :parent_places, source: :parent
has_many :children, through: :child_places, source: :child
end
place_relations
表的迁移应如下所示:
class CreatePlaceRelations < ActiveRecord::Migration
def change
create_table :place_relations do |t|
t.integer :parent_id
t.integer :child_id
t.datetime :effective_from
t.timestamps
end
end
end
因此,如果我们创建几个“顶级”国家/地区:
country1 = Place.create(name: "USA")
country2 = Place.create(name: "New California Republic")
和一个州府
state = Place.create("California")
和一座城市
city = Place.create("San Francisco")
最后将它们全部绑在一起:
state.parent_places.create(parent_id: country1.id, effective_from: DateTime.now - 1.year)
state.parent_places.create(parent_id: country2.id, effective_from: DateTime.now)
city.parent_places(parent_id: state.id, effective_from: DateTime.now)
那么您将拥有一个属于“加利福尼亚”州的城市(“旧金山”),该州历史上属于“美国”国家,后来属于“新加利福尼亚共和国”。
此外,如果您想构建一个包含该地点名称及其所有“父级”的字符串,您可以像这样“递归”地执行此操作:
def full_name(name_string = [])
name_string << self.name
parent = self.parents.first
if parent.present?
return parent.full_name name_string
else
return name_string.join(", ")
end
end
对于我们的城市“旧金山”来说,考虑到 effective_from
的顺序,结果应该是“旧金山,加利福尼亚州,新加利福尼亚共和国”
字段。
关于ruby-on-rails - Rails 中复杂的多对多关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30967130/