对于我的项目,我需要有自定义的url,因为随着对象的整数id的增加,这些url应该不容易猜测。为了解决这个问题,我给我的对象一个unique_id属性,它是一个字符串,并编写了resources :objects
自己生成的七个操作,看起来像:
get '/objects' => 'objects#index', as: :objects
post '/objects' => 'objects#create'
get '/objects/new' => 'objects#new', as: :new_object
get '/:unique_id/edit' => 'objects#edit', as: :edit_object
get '/:unique_id' => 'objects#show', as: :object
put '/:unique_id' => 'objects#update'
delete '/:unique_id' => 'objects#destroy'
一切都很好,我只需要告诉控制器通过Object.find_by_unique_id(params[:unique_id])
而不是Object.find(params[:id])
找到对象我可以创建,阅读和删除完美,但我不能更新。
当我打开edit_object_path时,它会正确地呈现表单,但当我单击"更新对象"时,它就会出错。从WEBrick输出中,我可以看到它创建了一个PUT请求,但它请求的是对象的id,而不是unique_id。例如,它不是请求PUT /osefushe7398hr9
,而是声明PUT /26
。update方法甚至没有被调用,而是要求创建一个unique_id为26的新对象。
我真的不知道在哪里改变这种行为,也不知道如何传递unique_id而不是id。
Rails模型有一个方法,您可能想要覆盖它来实现这一点,称为to_param
方法。此方法的默认行为是返回模型实例的ID(我认为您正试图更改该ID)。
按照你目前的布局,我会放弃你所有的路线变更。在模型中,添加:
def to_param
"#{unique_id.parameterize}"
end
因此,现在,使用普通路由,/objects/:变量的请求将按照您的意愿进行操作。
使用参数化方法,您可以安全地避免遇到不友好的URL。
覆盖to_param方法的一个小缺点是,现在控制器中使用Model.find方法的其他操作(:edit、:update和:destroy)都需要更改为Model.find_by_unique_id[如果您保持RESTful]。
在表单中,您可以明确地告诉它要发布到哪个URL以及要附带的参数。例如(使用HAML和form_tag)
= form_tag object_path(@object.unique_id), :method => :put do
你也可以更明确地使用:
= form_tag object_path(:id => @object.unique_id), :method => :put do
好的,解决方案是添加
def to_param
unique_id
end
到对象模型,而不使用参数化或#{}语法。事实上,我在尝试将unique_id传递到第二个解决方案中提到的表单时意外发现了这一点。事实证明,我不必像解决方案2中建议的那样更改form_for标记。parameterize是unique_id变为小写的原因。
现在一切都按预期工作(并且仍然是静止的)