【问题标题】:How do Rails' Controllers work?Rails 的控制器如何工作?
【发布时间】:2013-02-20 19:49:13
【问题描述】:

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

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

谁能解释一下,谢谢。

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

因此,对于我当前的上传表单,我有 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(我希望这个上传表单将其参数推送到 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] 操作。我尝试在下面遵循您的更多答案,但由于在我的情况下,我没有使用以现有模型为中心的表单,我有点迷茫。任何线索是什么问题?提前致谢。

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

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

    【讨论】:

      【解决方案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!

      【讨论】:

      • 那么在涉及表单的情况下会发生什么?如果我的 app/views/things/status.html.erb 视图中有上传表单 :upload}, :multipart => true) 执行 %> 然后我点击提交? params[:file] 对象去哪里了,我去哪里解析它的数据?很抱歉输出丑陋。
      • 添加了更多解释
      • 你指定的路由如何知道相互交互?编辑表单如何知道将条目提交到更新操作? (非常感谢您所做的一切,到目前为止,您一直非常乐于助人!!!)
      • 我在原始问题(第二部分)中添加了一个用例问题,任何进一步的帮助都意味着世界。非常感谢(再次)@RadBrad
      • 抱歉,我可能不清楚,如何将上传表单路由到 parse_upload 操作?同样(我不是 100% 确定这是相关的),但文件上传不是任何现有模型或数据库值的一部分;我不知道是否可以使用事物模型示例,我也想创建一个单独的操作,而不是使用现有的更新。 @RadBrad
      猜你喜欢
      • 1970-01-01
      • 2014-03-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多