Rails的控制器如何工作?

时间:2013-02-20 19:49:14

标签: ruby-on-rails controller controller-actions

1。)是否可以创建不直接与模型交互的控制器操作? (即上传要解析的文件,然后添加到数据库模型中)

2。)控制器的操作顺序是什么?我不明白控制器动作如何实例化视图,并对用户输入的参数变量作出反应。

有人可以解释一下,谢谢。

第二部分 - 路由无模型表格

因此,对于我当前的上传表单,我有2个操作,一个上传操作(从用户获取文件),我想将其路由到parse_upload操作(操作upload.html.erb视图中上传的文件):

routes.rb中:

::Application.routes.draw do
    devise_for :users
    resources :revenue_models do
        get 'upload', :on => :collection
        put 'parse_upload',:on => :collection
    end
    root :to => "home#index"
 end

动作:

# UPLOAD multiple files from an Exel Doc; integrate them accordingly
def upload
    @uploaded_doc = { :workbook => RubyXL::Parser.new }     
end
# Parse the uploaded file
def parse_upload
@worksheet = RubyXL::Parser.parse(params[:uploaded_doc]       
                  [:workbook]).worksheets[0].extract_data
end

upload.html.erb(我希望这个上传表单将其params推送到parse_upload操作)

<%= form_tag(:url => {:controller => "revenue_models", :action => "parse_upload"}, :html => {:method => "put", :multipart => true}) do %>
    <%= file_field(:uploaded_doc, :workbook) %>
<%= submit_tag("Upload") %>     
<% end %> 

目前,我在提交文件时收到路由错误:     没有路线匹配[POST]&#34; / revenue_models / upload&#34;

我假设一切正常,直到从上传表单到[parse_upload]操作的路由。我尝试按照下面的更多答案,但由于在我的情况下,我没有使用以现有模型为中心的表单,我有点迷失。有什么问题是什么?提前谢谢。

2 个答案:

答案 0 :(得分:3)

1)是的,当然,控制器根本不需要使用任何模型,或者它们可以与数千个模型一起工作。不要与scafolding混淆。控制器通常位于模型之上的事实是因为它的主要职责是与该模型进行交互,但这不是规则。

2)在Rack完成HTTP堆栈后,它将被分派到您的路由(在config / routes.rb中定义),然后路由器将其分派给您在该文件中指定的控制器/方法。如果要查看当前应用中的所有路线,请键入:“rake routes”。

控制器从机架接收参数。 http请求参数被整齐地打包到一个哈希中,并以您可以使用params()方法方便地访问的方式发送。除了阅读它们之外,控制器对params没有任何意义。

对于视图,控制器不会发送它们。当控制器调用render(),render_to_string(),render_with()等时,相应的视图模板以及布局(两者都可以指定或由控制器默认)将被加载和处理(转换为一个字符串,然后通过HTTP响应发送出去)。这里唯一的魔力是控制器中的实例变量可供视图使用(它们本身可以用作实例变量)

我希望这能为你解决一些问题.. :)

答案 1 :(得分:2)

1)是的,控制器'动作'不必处理模型,即

ThingController < ApplicationController
  def status
    @status = system("#{Rails.root}/lib/mystatusscript");
  end
end  

当URL到达服务器时调用操作,并查询路由表,并确定控制器和操作。所以,如果你把它放在你的routes.rb:

match "/whatever" => "things#status"

并输入

http://localhost:3000/whatever

将调用ThingsController(app / controllers / things_controller.rb)中的状态操作。

默认情况下接下来会发生什么,因为你没有告诉它做任何其他事情,rails会查找app / views / things / status.html.erb,然后渲染它,即:

The stats is <%= @status %>

但你可以阻止它,并让rails做其他事情,可能的例子:

ThingController < ApplicationController
  def status
    @status = system("#{Rails.root}/lib/mystatusscript");
    render :js=>"$('#status_retreived').show();"
  end
end  

ThingController < ApplicationController
  def status
    system("#{Rails.root}/lib/do_something_server_side");
    render :nothing=>true
  end
end  

ThingController < ApplicationController
  def status
    @status = system("#{Rails.root}/lib/mystatusscript");
    render action=>:edit
  end
end  

其他

让我们制作表格,看看会发生什么

假设你在app / views / things / edit.html.erb中有这个:

<%= form_for @thing do |f| %>
  <%= f.input :name %>
  <%= f.submit %>
<% end %>

假设您在routes.rb中有这些路线:

get '/things/:id/edit' => 'things#edit'
put '/things/:id/update' => 'things#update'

你的控制器有:

def update
  @thing = Thing.find(params[:id])
  @thing.attributes = params[:thing]
  @thing.save
end
def edit
  @thing = Thing.find(params[:id])
end

所以这是流程,你用'/ things / 100 / edit'

点击你的应用程序

调用编辑操作,实例变量@thing设置为id为100的记录。然后呈现edit.html.erb视图,向您显示名称字段和提交按钮的编辑屏幕。

当您点击“提交”时,您将输入'/ things / 100 / update'

由于路由被定义为'/ things /:id / update',当你进入更新动作时,params [:id]将包含100,AND params [:thing]将包含发布的内容表格,即你的参数可以包含:

params[:thing][:name]
params[:thing][:city]
....
params[:thing][:zip]

ID被抽象为params [:id],表单数据在params [:thing]

更多

rails为你做了很多自动url生成,它非常聪明,例如,在edit.html.erb中,你有这个:

<%= form_for @thing do |f| %>
  <%= f.input :name %>
  <%= f.submit %>
<% end %>   

如果查看生成的HTML,您会看到类似的内容:

<form id="edit_thing_100" method="put" action="/things/100/update"> 

rails如何知道更新而不是创建?因为它检查了@thing并注意到它之前已经保存到数据库中,所以它不是新记录,所以它必须是更新。

因此,在您的视图中,您通常会创建通过链接,提交按钮等发送到服务器的各种URI。当在routes.rb中查找它们时,将调用相应控制器中的相应操作。

文件上传

比您想象的要容易,首先您需要添加文件上传字段并稍微更改表单:

<%= form_for @thing do ,:html=>{:multipart=>true} |f| %>
  <%= f.input :name %>
  <%= f.file_field :upload %>
  <%= f.submit %>
<% end %>   

现在,在更新操作中,您可以执行此操作:

def update
  filename = params[:thing][:upload].original_filename
  filetype = params[:thing][:upload].content_type
  filedata = params[:thing][:upload].read

  File.open("#{Rails.root}/filestorage/#{filename}","wb") { |f| f.write(filedata) } 

  @thing = Thing.find(params[:id])

  @thing.attributes = params[:thing]
  @thing.uploadstoredin = "#{Rails.root}/filestorage/#{filename}"
  @thing.save
end

因为你创建了multipart形式,并且你声明了一个属性:upload as file_field,当params被发布时,:upload param有三个额外的方法(original_filename,content_type和read),Rails MAGIC!