Ruby on rails rubyonrails控制器设计
当我查看Rails控制器的示例时,通常会看到如下内容:Ruby on rails rubyonrails控制器设计,ruby-on-rails,Ruby On Rails,当我查看Rails控制器的示例时,通常会看到如下内容: class WidgetController < ActionController::Base def new @widget = Widget.new end def create @widget = Widget.new(params[:id]) if @widget.save redirect_to @widget else render 'new'
class WidgetController < ActionController::Base
def new
@widget = Widget.new
end
def create
@widget = Widget.new(params[:id])
if @widget.save
redirect_to @widget
else
render 'new'
end
end
end
GET/widgets/new
将路由到new
,POST/widgets
将路由到create
如果用户在新窗口小部件页面上输入错误信息并提交,他们的浏览器将显示带有/widgets
的URL,但新模板将被呈现。如果用户为页面添加书签并稍后返回或刷新页面,则将调用索引操作而不是新操作,这不是用户所期望的。如果没有索引操作,或者用户没有查看它的权限,那么响应将是404
代码重复
作为一个人为的例子,假设我的新方法中有一些棘手的逻辑:
def new
@widget = Widget.new
do_something_tricky()
end
使用当前的方法,我将在new
和create
中复制该逻辑。我可以从create
调用new
,但随后我必须修改new
以检查@widget
是否已定义:
def new
@widget ||= Widget.new
do_something_tricky()
end
此外,这感觉是错误的,因为它减少了控制器动作的正交性
怎么办?
那么Rails解决这个问题的方法是什么呢?我是否应该重定向到
new
,而不是呈现新模板?我应该在create
的内部调用new
?我应该接受它吗?有更好的方法吗?将do\u thicky()
放在它自己的方法中,并在创建操作中调用它(但只有在呈现新模板时,即验证失败时)
至于书签问题,我不知道有什么好方法可以防止它,但是可以修改路由并将创建操作设置为新操作,但是使用POST
get '/users/new' => 'users#new'
post '/users/new' => 'users#create'
更新:使用资源
resources :platos, except: :create do
post '/new' => 'plates#create', on: :collection, as: :create
end
然后您可以在表单中使用create_platos_path
一般来说,我认为解决问题的Rails方法是将棘手的方法放在模型上或作为辅助方法,这样控制器保持“精简”,并且您不必确保将自定义行为添加到\new
和\create
编辑:为了进一步阅读,我推荐《Rails反模式》这本书,因为它们讨论了许多常见的设计问题并给出了潜在的解决方案。您不需要在两个动作中编写相同的函数,而是使用before\u filter 如果你想在提交错误后得到“widget\u new\u url”,那么在你的表单中添加新widget路径的url,比如:url=>widget\u new\u path
Rails从表单中获取url。我以前遇到过这个问题,所以我改用编辑操作 这是我的密码 路线:
resources :wines do
collection do
get :create_wine, as: :create_wine
end
end
控制器:
def create_wine
@wine = Wine.find_uncomplete_or_create_without_validation(current_user)
redirect_to edit_wine_path(@wine)
end
def edit
@wine = Wine.find(params[:id])
end
def update
@wine = Wine.find(params[:id])
if @wine.update_attributes(params[:wine])
redirect_to @wine, notice: "#{@wine.name} updated"
else
render :edit
end
end
型号:
def self.find_uncomplete_or_create_without_validation(user)
wine = user.wines.uncomplete.first || self.create_without_validation(user)
end
def self.create_without_validation(user)
wine = user.wines.build
wine.save(validate: false)
wine
end
视图:
我所做的是用get action创建一个新动作“create_wine”
不是很适合RESTful设计,但解决了我的问题。如果有更好的解决方案,请告诉我。我认为这在“rails方式”中不是一个问题,并且没有内置的功能可以在不弄脏手的情况下实现这一点。当用户为刚提交但有错误的表单添加书签时,他们期望得到什么?用户并不清楚,他们不应该将失败的表单添加到书签中 我认为重定向到
new\u widget\u path
是最干净的解决方案。但是,您应该保留错误并将其显示在表单上。为此,我建议您将参数保持在会话中(我希望它比序列化的小部件对象小)
代码是自解释的,Widget。只有当来自会话的Widget\u返回nil时,才会调用new
,此时会话[:Widget\u params]存在。对散列调用delete
,将返回已删除的值,并将其从原始散列中删除
更新选项2
使用ajax提交表单怎么样?您的控制器可以受益于:
respond_to :html, :json
...
def create
@widget = Widget.new params[:widget]
@widget
respond_with @widget, location: nil
end
根据响应代码(由Rails:201 Created或422 Unprocessable Entity设置),您可以显示错误(验证失败时在响应主体中可用)或将用户重定向到@widget
StackOverflow就是这样做的:。他们异步提交表单。我意识到我可以编写自己的路由,但我不想。不为资源使用资源路由感觉是错误的。我希望有一种烘焙溶液。我不知道有哪种烘焙溶液能满足你的需要。如果你想在routes中使用资源
,你可以试试我更新的答案。do_something__技巧的要点是说明代码是如何违反DRY原则的。假设你必须在帖子创建之前和之后做一些事情。我指的是一个丑陋的例子,并不是试图解决一个特定的问题。另外,使用/new
路由调用create
方法仍然感觉不对。Rails反模式看起来是一个不错的读物。谢谢你指出这一点。有没有关于如何避免路由问题的想法?我认为这实际上不是一个问题——我不担心人们能够将他们发布无效数据的页面添加到书签中。如果它是多页向导类型的流的一部分,那么这可能是一个特殊的用例,可能需要重定向到URL中包含状态信息的中间页。在这种情况下,因为它已经重定向到一个成功创建的对象的show
操作,所以他们可以很容易地为它添加书签。我没有考虑在会话中存储对象。我想知道是否有一种方法可以更普遍地做到这一点。尽管如此,我认为这是一种危险的态度
= simple_form_for @wine, html: { class: 'form-horizontal' } do |f|
= f.input :complete, as: :hidden, input_html: { value: 'true' }
def new
@widget = widget_from_session || Widget.new
end
def widget_from_session
Widget.new(session.delete(:widget_params)) if session[:widget_params].present?
end
private :widget_from_session
# Before the redirect
session[:widget_params] = params
respond_to :html, :json
...
def create
@widget = Widget.new params[:widget]
@widget
respond_with @widget, location: nil
end