【问题标题】:Rails - Partial in another view can't access its controllerRails - 另一个视图中的部分无法访问其控制器
【发布时间】:2015-07-18 11:25:55
【问题描述】:

我正在尝试构建一个个人资料页面,该页面仅显示发送给请求用户的帖子,并允许访问者撰写他们自己的帖子。因为这个简化的示例应该有两个不同的控制器:用户和帖子,所以我为每个帖子操作制作了部分,以便在用户的显示操作中呈现。

我的视图目录的目录结构如下所示:

- posts
  - _index.html.erb
  - _new.html.erb

- users
  - show.html.erb
  ... (etc.)

在用户的show.html.erb 中显示这些部分的部分:

<section>
    <h3>Posts:</h3>
    <%= render '/posts/new', :post => Post.new %>
    <%= render '/posts/index', :posts => Post.where(target_id: params[:id]) %>
</section>

我最终发现,您可以将变量传递到 render 行中的部分,虽然这可行,但它非常混乱,可能没有遵循最佳实践。

理想情况下,我希望将这些部分与帖子控制器连接起来,这样我就可以在视图之外的地方编写更复杂的数据库查询:

class PostsController < ApplicationController
def new
    @post = Post.new
end

def index
    @posts = Post.where(target_id: params[:id])
end

def create
    @post = Post.new(post_params)   
    @post.user_id = current_user.id
    @post.target_id = params[:post][:target_id]

    if @post.save
        redirect_to :back, notice: 'You published a post!'
    else
        render new
    end
end

private
    def post_params
        params.require(:post).permit(:body)
    end
end

目前,我还没有找到这样做的方法。我知道这是一个新手问题,但提前感谢您的帮助。

【问题讨论】:

    标签: ruby-on-rails


    【解决方案1】:

    您试图将控制器视为模型:在后控制器中进行后期工作,而用户在用户控制器中工作。但是控制器是面向任务的,而不是面向模型的。

    由于您希望在用户表单中发布信息,因此通常将其收集在用户控制器中。例如

    class UsersController < ApplicationController
      def show
        ...
        @posts = Post.where(user_id: user.id)
      end
    end
    

    @posts 实例变量在显示模板及其调用的任何部分中都是可见的。但许多程序员更喜欢通过render 参数显式发送它,因为它更实用:

    <%= render '/posts/post_list', posts: @posts %>
    

    一方面,当您一眼就能看到部分的所有依赖项时,重构会更容易。

    【讨论】:

    • 呵呵,真奇怪,我不知道控制器可以被这样对待......谢谢你的帮助,Mori!
    【解决方案2】:

    我有点同意@Mori 的建议。正如他所说,您正试图将过多的逻辑放入控制器中。我认为这是您试图将其排除在视图之外的结果,这是正确的想法,但您希望业务逻辑包含在模型中。

    此外,PostsController 的那些 indexnew 操作永远不会被调用。例如,当您调用渲染 posts/new 时,这是渲染视图,而不是控制器操作。因此,这些控制器操作没有理由存在。

    我可能会以不同于 Mori 描述的方式实施修复。建议的做法是尝试将尽可能少的实例变量从控制器传递到视图 (see 3rd bullet in the linked section)。

    因为它实际上是我们在这里讨论的UsersControllershow 操作,所以我作为试图理解您的代码的人会假设您传递给show 视图的实例变量类似于@user .

    您可能希望在实例化@user 对象时使用includes 方法。 includes 语句将允许您使用尽可能少的查询来加载您需要实例化的附加模型(防止出现 N+1 查询情况)。如果有数千个匹配的帖子,您可能不想加载每一个,因此我将其任意限制为 10。

    用户控制器

    def show
      @user = User.find(params[:id]).includes(:received_posts).limit(10)
    end
    
    #....
    

    查看

    <section>
        <h3>Posts:</h3>
        <% unless @user.id == current_user.id %>
          <%= render 'posts/form', post: Post.new(user_id: @user.id) %>
        <% end %>
        <%= render @user.received_posts %>
    </section>
    

    如果你想渲染一个编辑动作,把一个新的post 的部分放在一个名为posts/form 的视图中将允许你重用该表单(form_for 通过调用传递的知道在提交时使用哪个动作模型的persisted? 方法)。

    请注意,此代码假定 User 模型与设置为 received_posts 的帖子具有第二个关系,但您可以将其更改为反映现实的任何内容。通过将received_posts 集合传递给render 方法,Rails 足够聪明地知道,如果您想渲染Post 模型的集合,以查找posts/_post 部分并为每个Post 渲染一个。 IMO 看起来更干净一些。只要确保将您的posts/show 代码移入其中即可。 posts/show 暗示这是它自己的行为,而不是用作其他东西的一部分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-18
      • 1970-01-01
      • 1970-01-01
      • 2020-06-04
      相关资源
      最近更新 更多