【问题标题】:Rails - Multiple Views(admin, regular) from same action(index)Rails - 来自同一操作(索引)的多个视图(管理员,常规)
【发布时间】:2014-12-29 19:36:01
【问题描述】:

我有 2 个版本的同一索引页面显示产品列表。 常规视图 - 显示列表 管理员视图 - 显示带有编辑、删除等选项的列表。

索引操作只是进行数据库查询并返回该实例变量。 目前,我有相同的索引操作来呈现索引页面。我想将索引操作表单产品重用到管理控制器中。

def index
    @products = Product.all
end

路线:

/product/index  => product#index

/admin/product/index  => product#index

我尝试了给出的 prepend_view_path 技术 [这里 | How can I intercept rails's template rendering,但在这两种情况下,这总是会呈现 admin/product/index.html.erb 文件。

我希望 Product#index 控制器#action 呈现:

/index.html.erb for /product/index

/admin/product/index.html.erb for /admin/product/index

有人可以帮助我如何以优雅的方式完成此操作,而不仅仅是为管理控制器类和产品控制器类编写相同的操作。

PS:我刚接触 ruby​​ 和 ruby​​ on rails 一周。非常感谢任何帮助。

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3


    【解决方案1】:

    我在开发 Rails 6 应用程序时遇到了这个挑战。

    我有两种类型的用户:客户管理员。我正在使用 Devise gem 进行身份验证。我希望 CustomerProducts 视图不同于 Admins 视图。

    我已经在 controllers 中有一个 app/controllers/admins 目录,用于 AdminsDevise 配置。

    我是这样做的

    首先,使用admins 命名空间为管理员products 视图定义一个新的路由

    namespace :admins do
      resources :products do
      end
    end
    

    注意:这将影响管理员的路径/URL。比如说,products_path 不会是 admins_products_path

    然后,在 controllers 中将 products_controller.rb 添加到 app/controllers/admins 目录:

    class Admins::ProductsController < ApplicationController
      before_action :set_product, only: [:show, :edit, :update, :destroy]
    
      # GET /products
      # GET /products.json
      def index
        @products = Product.all
      end
    
      # GET /products/1
      # GET /products/1.json
      def show
      end
    
      # GET /products/new
      def new
        @product = Product.new
      end
    
      # GET /products/1/edit
      def edit
      end
    
      # POST /products
      # POST /products.json
      def create
        @product = Product.new(product_params)
    
        respond_to do |format|
          if @product.save
            format.html { redirect_to admins_product_path(@product), notice: 'Product was successfully created.' }
            format.json { render :show, status: :created, location: admins_product_path(@product) }
          else
            format.html { render :new }
            format.json { render json: @product.errors, status: :unprocessable_entity }
          end
        end
      end
    
      # PATCH/PUT /products/1
      # PATCH/PUT /products/1.json
      def update
        respond_to do |format|
          if @product.update(product_params)
            format.html { redirect_to admins_product_path(@product), notice: 'Product was successfully updated.' }
            format.json { render :show, status: :ok, location: admins_product_path(@product) }
          else
            format.html { render :edit }
            format.json { render json: @product.errors, status: :unprocessable_entity }
          end
        end
      end
    
      # DELETE /products/1
      # DELETE /products/1.json
      def destroy
        @product.destroy
        respond_to do |format|
          format.html { redirect_to admins_products_url, notice: 'Product was successfully destroyed.' }
          format.json { head :no_content }
        end
      end
    
      private
        # Use callbacks to share common setup or constraints between actions.
        def set_product
          @product = Product.find(params[:id])
        end
    
        # Only allow a list of trusted parameters through.
        def product_params
          params.require(:product).permit(:name, :sku, :short_description, :full_description)
        end
    end
    

    注意:记下使用管理模块的 ProductsController 命名空间以及在 createupdatedestroy 操作中修改的路径

    最后,在 views 中,添加与 app/views/admins/products 目录中的 Admins Products 关联的视图。

    注意:您可能需要修改视图中的路径以与管理员产品的路径相对应。例如,admins productsshow 视图将是 admins_product_path(product) 而不是 productproduct_path(product)

    使用cells gem 有一种更简洁的方法。这消除了重复代码的需要,当您需要为多达 3 个或更多角色定义视图时,它会派上用场。您可以在此处阅读有关如何使用它的更多信息:Object-Oriented Views in Rails

    就是这样。

    我希望这会有所帮助

    【讨论】:

      【解决方案2】:

      路线有误。

      /product/index => 产品#index

      /admin/product/index => 产品#index

      两者都指向与产品相同的控制器名称。

      我会建议这些路线

      resources  :admin do 
        resources :product do 
        end
      end
      
      resources :product do 
      end
      

      这样做可以确保您有两个产品控制器。 一个带有 Admin::ProductController 第二个产品控制器

      admin_product_path 用于管理视图 普通视图的product_path

      【讨论】:

        【解决方案3】:

        在您的控制器中,只需渲染您想要为用户服务的视图,我的意思是:

        ProductsController
        def index
          # ...
          render 'index'
        end
        
        Admin::ProductsController
        def index
          # ...
          render 'products/index'
        end
        

        【讨论】:

        • 是的,但是如果我创建 2 个单独的控制器操作,那么您添加的 #... 部分将需要在两个操作中复制
        • 然后解释你的控制器层次结构。 ProductsControllerAdmin::ProductsController 的祖先还是后代?
        • ProductsController 和 AdminController 不同,但是我想在 AdminController 中使用 ProductsController 的 index 动作,因为方法完全一样
        • 实际上我已经通过添加一个变量向管理 url 添加了一个 hack。我可能会把它分开。
        • 您有多种方法可以实现这一目标。如果这些控制器中的任何一个是另一个控制器的后代,那么在后代索引操作中,您可以调用 super,例如:def index; super; end。如果没有,您可以在祖先控制器中创建一些辅助方法(例如ApplicationController),将所有逻辑放在其中并在后代的index 方法中使用。您可以创建一个模块并将其包含到您的控制器中。最后,您的控制器结构看起来应该像 ProductsController &lt; Admin::ProductsController(或相反 - 这取决于哪个是另一个的祖先)。
        猜你喜欢
        • 1970-01-01
        • 2011-03-17
        • 1970-01-01
        • 2016-01-16
        • 2012-03-27
        • 2011-10-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多