我正在尝试找出管理特定用例的控制器和模型的最佳方法。
我正在构建一个审核系统,用户可以使用Polymorphic Reviewable建立对多个不同类型的评论。
Country (has_many reviews & cities)
Subdivision/State (optional, sometimes it doesnt exist, also reviewable, has_many cities)
City (has places & review)
Burrow (optional, also reviewable ex: Brooklyn)
Neighborhood (optional & reviewable, ex: williamsburg)
Place (belongs to city)
我也想知道增加更多的复杂性。我还想偶尔包括细分...即对于美国,我可能会添加德克萨斯州或德国,Baveria并且也可以审查它,但并非每个国家都有地区,甚至那些可能永远不会被审查的地区。所以它一点也不严格。我希望它尽可能简单灵活。
如果用户可以登陆一个表单并选择城市或国家,然后使用Foursquare中的数据向下钻取以查找城市中的特定位置并进行审核,那就太好了。
我真的不确定应该走哪条路?例如,如果我有一个国家和一个城市会发生什么......然后我决定添加一个陋居?
我可以将地点标签(即威廉斯堡,布鲁克林)归属于纽约市并且标签属于纽约吗?
标签更灵活,可选择解释它们可能属于哪些区域,标签属于某个城市,但也有地点和可审核?
所以我正在为那些做过相关事情的人寻找建议。
使用Rails 3.2和mongoid。
答案 0 :(得分:3)
我已经建立了非常相似的东西,发现两种完全不同的方式,两者都运作良好。
对我有用的第一种方式是使用国家,子国家,城市,邻居。这很好地映射到主要的地理编码服务,并且足以用于大多数简单的用途。这可以是STI(如您的示例所示)或多个表(我是如何做到的)。
在你的例子中,你写了“细分/状态”。我的两分钱是为了避免使用这些术语,而是使用“Subcountry”,因为它是ISO标准,当另一个开发人员认为细分是一个小房子附近,或者当你有一个非美国国家时,你会省去一些困惑不使用状态,而是使用省份。
这是我在尝试模型名称(如地区,区域,区域,区域等)的许多实验后所确定的,并且放弃这些过于模糊或过于具体。在您的STI案例中,可以使用更多名称。
令人惊讶的是,编写多层次的关联是一个很大的帮助,例如说country.cities(跳过子类)。这是因为有时中间模型不存在(即没有子国家)。在您的STI中,这可能会更棘手。
如果你对表格进行非规范化,你也会获得很大的加速,所以例如我的城市表有一个国家字段。这使得更新信息有点棘手,但值得。您的STI可以通过使用标签来实现与此相当的效果。
第二种方法是使用任意区域模型并存储纬度经度形状。这为您提供了极大的灵活性,您可以预先计算形状何时包含其他形状或与其相交。所以你的“向下钻取”变成“在这一个内部立即显示我的形状”。
Postgres为此提供了一些很好的地理编码助手,您可以通过执行min / max lat / lng的边界框来加速查找。我们还存储了数据,例如区域的预期中心点(我们将地图放置在地图上的位置)和半径(用于计算查询,例如“向我显示y距离内的所有x项目)。”/ p >
通过这种方法,我们能够做一些有趣的区域,比如“纽约的百老汇”,这里并不是长街,而是“亚马逊盆地”,它是由河而不是国家定义的。
答案 1 :(得分:2)
具有祖先和多元关系的STI模型
我为之前的项目构建了类似的东西,并且为了具有祖先的STI,因为它非常灵活并且允许对节点树进行建模。并非所有中间节点都必须存在(如您的State / Subdivision / Subcountry示例中所示)。
对于Mongoid,至少有两个祖先宝石:mongoid-ancestry和mongestry(下面的链接)。
作为使用带有祖先的STI的额外好处,您还可以为其他与位置相关的节点建模,例如餐馆或其他地方。
您还可以向所有节点添加地理位置信息lat / lon,以便对其进行地理标记。 在下面的示例中,我只使用了一组地理位置坐标(中心点) - 但您当然也可以添加几个地理位置来为边界框建模。
您可以按自己喜欢的顺序排列节点,例如通过this_node.children.create(...)
。
使用带有祖先的MySQL时,可以传入新创建的节点的类型。 mongoid-ancestry必须有类似的方法(还没试过)。
除了树形结构的节点之外,您还可以使用多态集合来对评论进行建模,还可以使用标签(好吧,act_as_taggable有一个宝石,所以您不必自己模拟标签)。
与使用自己的集合对每个类进行建模相比,这种STI方法更灵活,并且使模式更简单。稍后添加新类型的节点非常容易。
此范例可与Mongoid或SQL数据存储一起使用。
# app/models/geo_node.rb
class GeoNode # this is the parent class; geo_nodes is the table name / collection name.
include Mongoid::Document
has_ancestry # either this
has_mongestry # or this
has_many :reviews, :as => :reviewable
field :lat, type: Float
field :lon, type: Float
field :name, type: String
field :desc, type: String
# ...
end
# app/models/geo_node/country.rb
class Country < GeoNode
end
# app/models/geo_node/subcountry.rb
Class Subcountry < GeoNode
end
# app/models/geo_node/city.rb
class City < GeoNode
end
# app/models/review.rb
class Review
include Mongoid::Document
belongs_to :reviewable, :polymorphic => true
field :title
field :details
end
检查以下链接:
mongoid-tree gem https://github.com/benedikt/mongoid-tree
对Mongoid STI的要点:https://gist.github.com/507721
祖先宝石(适用于MySQL)
非常感谢Stefan Kroes他出色的祖先宝石,以及Anton Orel将其改编为Mongoid(mongoid-ancestry)。祖先是我见过的最有用的宝石。
答案 2 :(得分:1)
听起来像是嵌套路由/资源的一个很好的候选者。在routes.rb
中,执行以下操作:
resources :cities do
resources :reviews
end
resources :countries do
resources :reviews
end
resources :places do
resources :reviews
end
哪个应该产生rake routes
:
reviews_cities GET /cities/:id/reviews {:controller=>"reviews", :action=>"index"}
reviews_countries GET /countries/:id/reviews {:controller=>"reviews", :action=>"index"}
reviews_places GET /countries/:id/reviews {:controller=>"reviews", :action=>"index"}
...etc., etc.
在控制器操作中,您查找匹配:id
reviewable
条记录,并仅发回附加到该reviewable
对象的评论。
另外,请参阅Rails Routing Guide的nested resources
部分和this RailsCast on Polymorphic relationships部分,其中包含有关路由的快速部分,并让所有内容正确排列。
答案 3 :(得分:1)
我可能会保持我的数据模型非常不受限制,并处理与控制器/视图中显示的过滤器相关的任何细节。制作一个映射表,您可以在其中以多态方式将属性(即城市,自治市镇,州,国家/地区)映射到reviewable
。
通过假定多对多,您的架构尽可能灵活,并且您可以使用模型中的验证或过滤器来限制要创建的映射。
它基本上是使用标记,就像你所说的那样,但并不真正使用标签模型本身,而是与不同模型的多态关联,它们都像标签一样。
保持数据库架构清洁,并将业务逻辑保留在ruby中。