2

嘿伙计们,我需要你的宝贵帮助。在Ruby on Rails 教程的第 10 章中,作者在一个视图中有 2 个控制器。我构建了一个类似的应用程序,其中我在一个视图中也有 2 个控制器,但是,当我渲染一个属于第二个控制器的方法时,从第一个开始我就遇到了参数问题。(在教程中作者没有在其他控制器的动作中使用任何参数)

更具体地说,我有 2 个控制器:UsersController 和 MicropostsController。另外,在 Users 的 show.html.haml 页面中,我使用了两个控制器:UsersController 用于显示用户的微博,而 MicropostsController 则允许用户创建新的微博。

MicropostsController 内部:

def create
  @micropost = current_user.microposts.build(params[:micropost])
  if @micropost.save
    flash[:success] = "Micropost created!"
    redirect_to user_path(current_user)
  else
    #render text: renderActionInOtherController(UsersController,:show, {:id => 1})
    @user = User.find(current_user)
    @microposts = @user.microposts.paginate(page: params[:page])
    render 'users/show'
  end
end

内部用户控制器

def show
  @user = User.find(params[:id])
  @microposts = @user.microposts.paginate(page: params[:page])
  @micropost = current_user.microposts.build if signed_in?
end

在 app/views/users/show.html.haml

- provide(:title, @user.name)
.users_page
  .row
    %aside.span4
      - if !signed_in?
        %section
          %h1
            = gravatar_for @user
            = @user.name
      - else
        %section
          = render 'shared/user_info'
        %section
          = render 'shared/micropost_form'

    .span8
      - if @user.microposts.any?
        %h3 Microposts (#{@user.microposts.count})
        %ol.microposts
          = render @microposts
        = will_paginate @microposts

所以基本上我的问题总结如下:

1)在一个视图中有多个控制器是一种好习惯吗?我在网上找到了相互矛盾的答案。(实际上,我什至不确定这段代码是否仍然是 RESTful)

2)如果 1 是肯定的(或者至少这不是一个坏习惯),我可以以更有效的方式实现同​​样的事情吗?因为在我看来,每次我从另一个控制器渲染动作时,我都必须重新定义变量。

3)我在stackoverflow中发现了一个类似的主题,其中有人建议使用这种方法(由于我是RoR的新手,所以我不知道它的确切作用)。

def renderActionInOtherController(controller,action,params)
  controller.class_eval{
    def params=(params); @params = params end
    def params; @params end
  }
  c = controller.new
  c.request = @_request
  c.response = @_response
  c.params = params
  c.send(action)
  c.response.body
end

如果我在 MicropostsController 中使用这个版本的创建操作,

def create
  @micropost = current_user.microposts.build(params[:micropost])
  if @micropost.save
    flash[:success] = "Micropost created!"
    redirect_to user_path(current_user)
  else
    render text: renderActionInOtherController(UsersController,:show, {:id => 1})
    #@user = User.find(current_user)
    #@microposts = @user.microposts.paginate(page: params[:page])
    #render 'users/show'
  end
end

当我点击发布按钮时,我在浏览器中什么都没有。此外,当我尝试查看 tha users/1 页面时,我收到以下错误(!!):

undefined method `[]' for nil:NilClass

任何帮助都将非常有价值!如果您需要任何其他信息,请告诉我!

4

2 回答 2

3
  1. 从技术上讲,这个术语有些混乱。默认情况下,控制器中的每个方法都对应于一个名称相似的视图文件,因此最好将问题表述为“呈现不是默认视图的视图是否是一种好习惯?” 当然,答案是视情况而定。这是一种常用来干燥控制器代码的技术,如果它有好处,在你的应用程序中,我肯定会使用它。实际上,Rails 中默认资源脚手架生成的控制器代码在and方法中使用了它。我认为你可以提出这样的论点:Rails 核心中的任何东西,如果不是最佳实践,至少在理智的范围内。createupdate

  2. 话虽如此,可能还有改进的机会。处理相同事情的常用方法是将您的创建和更新请求路由到相同的控制器操作并使用相同的控制器视图。如果这不可行,至少可以确保您不需要重新定义变量。从官方文档

将 render 与 :action 一起使用是 Rails 新手经常感到困惑的原因。指定的操作用于确定要呈现哪个视图,但 Rails 不会在控制器中运行该操作的任何代码。您在视图中需要的任何实例变量都必须在调用渲染之前在当前操作中设置。

  1. 你不应该这样做......在这种情况下,或者我能想到的任何其他情况下。任何时候你在 Rails 中看到类似的超级 hackey 都表明某些事情可能不太正确,并且可能有更好的方法来处理同样的事情。

奖励:如果您需要去某个已经负责设置自己的地方,并且您不需要访问任何范围内的变量,那么重定向可能是更好的选择。这样,您无需在多个地方重新描述您的控制器逻辑。这是您发布的代码中的一个示例:

    # inefficient and not DRY
    @user = User.find(current_user)
    @microposts = @user.microposts.paginate(page: params[:page])
    render 'users/show'

    # does the same thing as above (in this case)
    redirect_to users_path
于 2012-11-08T01:12:14.573 回答
0

我想补充一点,我认为教程作者为什么对 Create Microposts 操作实施了一种不那么 DRY 的解决方案的理由是,如果使用 redirect_to 而不是 render,那么他们会丢失表单的 flash 错误。

我不确定是否有更干燥的解决方案来维护表单闪存错误。

于 2013-06-03T00:28:05.367 回答