在路径助手中使用非默认属性,而不是:id

时间:2017-05-04 09:26:34

标签: ruby-on-rails activerecord key

我拥有什么

我现在有一条像

这样的路线
get 'book/:id' => 'books#show', as: :book

创建该网址的代码是:

book_path @book

在控制器中:

@book = Book.find(params[:id])

到目前为止一切顺利。现在,这个id显然是内部的ActiveRecord或数据库主键。

我想要什么

相反,我会(在这个虚构的例子中)使用ISBN。我可以这样做:

get 'book/:isbn' => 'books#show', as: :book
book_path(isbn: @book.isbn)
@book = Book.find_by(isbn: params[:isbn])

这有点冗长;我想在整个应用程序中使用此模型的约定“isbn”,并且任何人都不需要关心它。所以,我希望我的3行看起来像这样:

get 'book/:isbn' => 'books#show', as: :book
book_path @book    # (!)
@book = Book.find(params[:isbn])    # (!)

是否可以配置路线以实现我的愿望?显然,这应该自动无处不在,至少对于用户在URL中看到ISBN的GET请求。

如果重要,#isbn属性不是通常意义上的主键,但保证表中只有一个具有特定ISBN的记录。所以至少这部分主键合同已经完成。

2 个答案:

答案 0 :(得分:0)

要覆盖Rails默认:id参数,您可以使用to_param方法:

class Book < ActiveRecord::Base
  def to_param  # overridden
    isbn
  end
end

在控制器中:

book = Book.find_by(isbn: '123456')

如果您对使用任何宝石没有任何问题,那么我建议您查看friendly_id宝石。在这种情况下,这样做很好。

答案 1 :(得分:0)

routes.rb中,使用匹配将/book/:isbnbook#show匹配

match '/book/:isbn' => 'book#show', via: :get, as: :show_book

在book.rb中,您可以根据isbn而不是id重写find方法。有点像:

def self.find(*args)
    c = Book.select("id").find_by(name: args[0])
    args[0] = c.id
    super(args)
end

这将首先通过isbn查找,然后使用id,它将调用原始查找方法。 (我没有对此进行测试,您必须修改它以使其适用于多个isbn s)

但我不确定这是否值得推荐以及会出现什么警告。

相反,为什么不使用find_by_isbn[params[:isbn]]?它比find_by(:isbn, params[:isbn])更具可读性。