2

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

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

有人能解释一下吗,谢谢。

第 II 部分 - 路由无模型表单

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

路线.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 (我希望此上传表单将其参数推送到 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] "/revenue_models/upload"

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

4

2 回答 2

3

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

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

控制器从机架接收参数。http 请求参数被整齐地打包到一个散列中,并以您可以方便地使用 params() 方法访问的方式发送。除了读取参数之外,控制器不会对参数做任何事情。

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

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

于 2013-02-20T20:03:14.443 回答
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 视图,显示名称字段的编辑屏幕和提交按钮。

当您单击“提交”时,您将 PUT 到“/things/100/update”

由于路径被定义为“/things/:id/update”的方式,当您进入更新操作时,params[:id] 将包含 100,并且 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,这些 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

因为您制作了多部分表单,并且将属性 :upload 声明为 file_field,所以在发布参数时,:upload 参数有三个额外的方法(original_filename、content_type 和 read),Rails MAGIC!

于 2013-02-20T20:10:37.837 回答