【问题标题】:Do I have to specify actions in routes.rb?我必须在 routes.rb 中指定操作吗?
【发布时间】:2015-07-31 15:09:19
【问题描述】:

在阅读Rails 4 in Action 时,我正在尝试实现自己的应用程序,因此它看起来与书中的不同。 本书对应的commit是Section 7.2.3: Only admins can create or delete projects

在我的例子中,管理员只能删除该项目item对应书中的project。)。

我的仓库https://github.com/tenzan/shop 并部署了http://ichiba-demo.herokuapp.com/

我要应用的规则是:

  1. 普通用户(您可以使用staff@example.com/password 登录)可以执行除destroy 操作之外的所有操作。
  2. 管理员 (admin@example.com/password) 只能destroy

意识到我有:

admin/items_controller.rb:

    class Admin::ItemsController < Admin::ApplicationController

      def destroy
        @item = Item.find(params[:id])
        @item.destroy

        flash[:notice] = 'Item has been deleted.'
        redirect_to items_path
      end

      private

      def item_params
        params.require(:item).permit(:name, :quantity)
      end

    end

controllers/items_controller.rb:

class ItemsController < ApplicationController
  before_action :set_item, only: [:show, :edit, :update]

  def index
    @items = Item.all
  end

  def new
    @item = Item.new
  end

  def create
    @item = Item.new(item_params)

    if @item.save
      flash[:notice] = 'Item has been created.'
      redirect_to @item
    else
      flash.now[:alert] = 'Item has not been created.'
      render 'new'
    end
  end

  def show
  end

  def edit
  end

  def update

    if @item.update(item_params)
      flash[:notice] = 'Item has been updated.'
      redirect_to @item
    else
      flash.now[:alert] = 'Item has not been updated.'
      render 'edit'
    end
  end

  private

  def set_item
    @item = Item.find(params[:id])
  rescue ActiveRecord::RecordNotFound
    flash[:alert] = 'The item could not be found.'
    redirect_to items_path
  end

  def item_params
    params.require(:item).permit(:name, :quantity)
  end
end

routes.rb:

Rails.application.routes.draw do
  namespace :admin do
    root 'application#index'

    resources :items, only: :destroy
  end

  devise_for :users
  root 'items#index'

  resources :items, only: [:index, :show, :edit, :update, :new, :create] do
    resources :comments
  end
end

问题:

  1. 我是否必须在routes.rb 中指定操作,因为我已经指定了谁可以在其相应的控制器中使用哪些操作?当我将它们从 routes.rb 中删除时,我没有注意到任何变化...
  2. 当我在两个地方(即routes.rbcontrollers/items_controllers.rb)指定操作时,我是否违反了 DRY 概念?

如果您指出其他地方需要改进以符合最佳实践,我会很高兴。

PS:主题可能含糊不清,请随时修改。

【问题讨论】:

    标签: ruby authentication devise dry ruby-on-rails-4.2


    【解决方案1】:

    我是否必须在 routes.rb 中指定操作,因为我已经有了 指定谁可以在其相应的控制器中使用哪些操作?

    是的。例如,如果你在items_controller.rb 控制器中只有一个动作(比如说show),然后离开

    resources :items do # no specified actions
      #...
    end
    

    routes.rb 中,它将为项目控制器生成所有路由(对于newcreateeditdestroyupdate 等)。但是在 routes.rb 中指定操作会将生成的路由限制为仅需要。

    当我在 2 个地方指定操作时,我是否违反了 DRY 概念,即 在 routes.rb 和 controllers/items_controllers.rb 中?

    没有。因为您实际上是在控制器中指定操作,所以在 routes.rb 中您只指定 routes

    如果你能指出其他需要改进的地方,我会很高兴 练习。

    这一行:

    resources :items, only: [:index, :show, :edit, :update, :new, :create] # better to use %i() notation, eg only: %i(index show edit update new create)
    

    可以写成:

    resources :items, except: :destroy
    

    关于您的管理员用户 - 只允许他destroy,只需检查 current_user 是否为管理员。如果您的操作不止一个只能由管理员执行,您可以在控制器中创建 before_action:

    before_action :check_admin?, only: %i(destroy another_action)
    
    private
    
    def check_admin?
      # your logic to check, if user is admin
    end
    

    您也可以有兴趣通过Ruby style guide

    【讨论】:

    • 斯帕西博,安德烈!我也投票赞成你的回答,因为你给了我新的提示。 ;)
    【解决方案2】:

    即使您没有直接违反 DRY,您也可以通过将单个实体的操作移动到不同的控制器来混淆 REST 架构。您不需要管理员的特定控制器或命名空间 - 您只需在继续删除操作之前断言用户是管理员。

    由于您已经将admin 列添加到您的设计模型,您可以将删除操作移动到ItemsController

    def destroy
      if current_user.try(:admin?)
    
        @item = Item.find(params[:id])
        @item.destroy
    
        flash[:notice] = 'Item has been deleted.'
      else
        flash[:alert] = 'Only admins can delete items.'
      end
      redirect_to items_path
    end
    

    您的路线会更干净,因为您的管理命名空间将仅用于用户审核。物品的唯一路线是:

      resources :items do
        resources :comments
      end
    

    【讨论】:

    • 我假设,您的意思是不需要为admin/items 创建命名空间?正确的?我想我仍然需要创建 admin/users 命名空间来添加和删除用户...
    • 啊,是的 - 您需要它来进行用户审核。我在想您创建的命名空间只是为了让管理员修改项目。更新了答案
    • 我正在测试,完成后返回。
    • 这里好像需要else条件,否则会报错找不到destroy的模板 action: def destroy if current_user.try(:admin?) @item.destroy flash[:notice] = '项目已被删除。' else flash[:alert] = '只有管理员可以删除项目。'结束重定向到 items_url 结束
    • @oscar 是的,我重构了答案来做到这一点:)
    猜你喜欢
    • 2023-03-05
    • 1970-01-01
    • 2015-08-26
    • 2013-10-13
    • 2017-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多