Rails Rails route 匹配的问题

tualatrix · 2012年02月16日 · 最后由 willmouse 回复于 2012年02月17日 · 6649 次阅读

最近我又注意到一个问题,不知道是 bug 还是 feature…因为对 Route 机制还没有从根本上明白。

比如这篇帖子:http://ruby-china.org/topics/1224

是通过 1224 这个 id 来定位的,但是实际上,如果我输入:http://ruby-china.org/topics/1224hello

一样可以访问该帖子,只要「1224」后面不直接跟一个数字,都没有问题。

以前用 Django 时用正则来匹配 URL,所以不会发生这样的情况,URL 是唯一的。而 Rails 这样的行为,到底算不算正常呢?(在我自己写着玩的 Rails 项目也是这样的,所以这不是 Ruby-china 才这样的)

这是个 feature。

PS:有一些站点为了 SEO 或者其他目的,把 title 放在 url 中(这个叫 slug 吧?),使用的是 http://localhost/posts/123-hello-world 这种形式。reddit 的评论地址是 http://www.reddit.com/r/pics/comments/:id/ ,也可以是 http://www.reddit.com/r/pics/comments/:id/:title ,这个 title 一般是帖子标题,但可以是任意合法的字符串。

:id 可以支持字母这个很多时候用起来很爽的

不晓得还可以这样,只是很规范的

def to_param
  "#{id}-#{title}"
end

#2 楼 @huacnlee 比如可以应用在哪些场景?

/topics/:id

只是定义了 :id 这样一个参数名,没有定义它必须的格式。可以通过 constraints 来定义格式 http://guides.rubyonrails.org/routing.html#specifying-constraints ,默认情况下对 . 和 / 有特别处理。

ruby-china 应该是用了 mongoid_auto_increment_id 里面的这一行 https://github.com/huacnlee/mongoid_auto_increment_id/blob/master/lib/mongoid_auto_increment_id.rb#L72 导致自动转换字符串到数字。ActiveRecord 是怎么处理就不知道了,不怎么看源码。

active_record中类似Topic.find(params[:id])这种是to_i过的。

我还是喜欢有一个固定永久的 URL,有洁癖。不大懂 SEO,不知道固定永久的 URL 对 SEO 有没有帮助

#5 楼 @Rei 请教一下,既然没有定义:id 的格式,那可以把这个:id 改成 Model 的其他非 id 属性吗?例如:slug, 然后还能继续在 route 里使用 resuorces。

@tualatrix 这不是路由的问题。 你可以在 controller 里打印一下 params[:id] 应该是输出"1224hello"

这应该是 ORM 的特性,比如 ActiveRecord:

1.9.3p0 :006 > User.find 3
  User Load (0.2ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 3 LIMIT 1
1.9.3p0 :007 > User.find "3"
  User Load (0.2ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 3 LIMIT 1
1.9.3p0 :008 > User.find "3hello"
  User Load (0.2ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 3 LIMIT 1

可以这么转换一定是利用了 Ruby 的特性:

1.9.3p0 :009 > "123hello".to_i
 => 123

Ruby 的字符串转换成整数还可以用 Integer 方法,和 to_i 有一点区别:

1.9.3p0 :011 > Integer("123")
 => 123 
1.9.3p0 :012 > Integer("123hello")
ArgumentError: invalid value for Integer(): "123hello"

严格说这是一个 bug....

#7 楼 @_samqiu 我没找到 resuorces 路由的这个配置

可以在 model 里面定义 to_param 方法,返回你需要的参数,然后 controller 里面还是用 params[:id] 来取参数,但不用 find 方法来找对象。这样 url helper 就不用动了。

#7 楼 @_samqiu 可以哇 其实还可以改表的主键:

1.9.3p0 :016 > User.set_primary_key "email"
 => nil 
1.9.3p0 :017 > User.find "hoooopo@gmail.com"
  User Load (0.5ms)  SELECT `users`.* FROM `users` WHERE `users`.`email` = 'hoooopo@gmail.com' LIMIT 1

#11 楼 @hooopo 这个很好,谢谢:)

#8 楼 @hooopo 正解,跟 routes 没关系。是 to_i 的隐式转换。经常用的一个 “Feature”。:)

#8 楼 @hooopo #13 楼 @ashchan 对,to_i 这个是比较常用

1.9.2p290 :006 > "123t".to_i
 => 123 
1.9.2p290 :007 > "t".to_i
 => 0 
1.9.2p290 :008 > "".to_i
 => 0 
1.9.2p290 :009 > nil.to_i
 => 0 

#4 楼 @tualatrix 哦,我看错了...

有一期 railscast ryanb 提到了这个 route 问题,下午找了老久,愣是没找出来

#16 楼 @willmouse

034-named-routes.mp4 046-catch-all-route.mp4 070-custom-routes.mp4 079-generate-named-routes.mp4

路由里的 id,只是参数的 key 的名字,不一定非得是整数。

另外,Rails-2.3.5 的相关代码在 ActiveRecord::ConnectionAdapters::Quoting#quote 。如果这个字段,在数据库里的字段类型是 integer ,那么拼 SQL,会先把值 #to_i ;如果是 float,那就是 #to_f :

elsif column && [:integer, :float].include?(column.type)
  value = column.type == :integer ? value.to_i : value.to_f
  value.to_s

column 是数据库的字段;value 是字段值。

#17 楼 @lidashuang

应该不是这几期,这几个我都查过了,我记得是再说别的内容时提到的这个点

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册