最近我又注意到一个问题,不知道是 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 一般是帖子标题,但可以是任意合法的字符串。
/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 有没有帮助
@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"
1.9.3p0 :016 > User.set_primary_key "email"
=> nil
1.9.3p0 :017 > User.find "[email protected]"
User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`email` = '[email protected]' LIMIT 1
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 是字段值。