【问题标题】:Rails naming convention for an action that shows a form and the other that processes the form用于显示表单的操作和处理表单的操作的 Rails 命名约定
【发布时间】:2019-07-15 16:44:38
【问题描述】:

我需要创建两条路线。第一个是GET路由,显示某种表单,另一个是POST路由,处理表单。

例如,我需要创建一个显示允许销售人员取消订阅交易的表单的路由。

那么,如何命名这些路线。你会创建这样的东西吗:

get 'deals/:id/unsubscription', to: 'deals#unsubscription'
post 'deals/:id/unsubscribe', to: 'deals#unsubscribe'

或者你是否像这样创建一个单一的路线:

match 'deals/:id/unsubscribe', to: 'deals#unsubscribe', via: [:get, :post]

并根据请求方法将逻辑放在同一个动作中。

【问题讨论】:

  • 你有resources :deals吗?
  • 我有。但是,我的问题与特定操作有关,例如我提供的示例,其中我需要特定逻辑,因此避免膨胀 update 操作

标签: ruby-on-rails rails-routing


【解决方案1】:

另一种方法是将其声明为 RESTful 资源,就像其他任何资源一样进行 CRUD:

GET /deals/:deal_id/subscriptions/new # form to subscribe 
POST /deals/:deal_id/subscriptions # subscribe
GET  /subscriptions/:id # view with button/form to unsubscribe
DELETE /subscriptions/:id # unsubscribe

你可以这样做:

resources :deals
  resources :subscriptions, shallow: true, only: [:show, :new, :create, :destroy] 
end

这遵循 Rails 约定,并使用正确的 HTTP 方法进行非惯用操作。例如,POST 预计会创建一个使POST /deals/:id/unsubscribe 令人畏惧的资源 - DELETE /deals/:id/unsubscribe 会更好一些。

一般来说,拥有一个响应不同 HTTP 方法的控制器方法并不是最好的主意,因为它具有更高的圈复杂度。理想情况下,每种方法都应完成一项工作。

每个控制器还应该代表一个资源,这使得在同一个控制器中干扰交易和订阅是一个坏主意。

控制器只是你运行的嵌套资源:

class SubscriptionsController < ApplicationController
  before_action :set_subscription, only: [:show, :destroy]
  before_action :set_deal, only: [:new, :create]

  # GET /deals/:deal_id/subscriptions/new
  def new
    @subscription = @deal.subscriptions.new
  end

  # POST /deals/:deal_id/subscriptions/new
  def create
    @subscription = @deal.subscriptions.new(subscription_params)
    if @subscription.save
      redirect_to @subscription
    else
      render :new
    end
  end

  # GET /subscriptions/:id
  def show
  end

  # DELETE /subscriptions/:id
  def destroy
    @subscription.destroy
    redirect_to @subscription.deal
  end

  def set_subscription
    @subscription = Subscription.includes(:deal).find(:id)
  end

  def set_deal
    @deal = Deal.find(params[:deal_id])
  end

  def subscription_params
    params.require(:subscription)
          .permit(:foo, :bar)
  end
end

【讨论】:

  • 我喜欢这种方法。但是,代码假定交易具有订阅关联,但事实并非如此。我仍在研究 Deal 模型。你还创建订阅控制器吗?
  • 我还想显示一个“取消订阅”表单。我看不出如何以 CRUD 方式对其进行建模,除非您创建一个“负面”资源,正如您在另一条评论中提到的那样。
  • 无论如何,您都需要订阅模型和关联。所以是的,您可以在单独的控制器中执行此操作。如果您需要单独的表单,只需创建一个附加操作。 guides.rubyonrails.org/routing.html#adding-more-restful-actions
【解决方案2】:

您可以使用member 路由助手。

要添加成员路由,只需将成员块添加到资源块中即可:

resources :deals do
  member do
    get : unsubscription
    post :unsubscribe
  end
end

如果你通过不同的控制器处理它,你可以让它变得更好,如下所示:

resources :deals do
  resources :unsubscriptions,
            only: [:new, :create],
            controller: "deals/unsubscriptions"
end

现在创建一个命名空间控制器,如下所示:

Deals::UnsubscriptionsController < ApplicationController
  def new; end # renders the form.
  def create; end # this process the form submission.
end

以上路线为您创建了可读性好的 url:

deal_unsubscriptions    POST   /deals/:deal_id/unsubscriptions(.:format)          deals/unsubscriptions#create
new_deal_unsubscription GET    /deals/:deal_id/unsubscriptions/new(.:format)      deals/unsubscriptions#new

【讨论】:

  • 我的问题更多关于命名约定。那么,您认为将名称(取消订阅)作为GET 路由并将动词(取消订阅)作为POST 路由是更常见的做法吗?
  • 是的,我很好。
  • 但是如果通过不同的控制器处理会好很多..
  • 你在正确的道路上,但完全倒退了。 rails 中的newcreate 都是关于创建资源的。 “负面”资源是主要的 WTF。
  • hm.. 不确定你在这里所说的负面资源是什么意思.. :) @max
【解决方案3】:

将 GET 和 POST 请求路由到单个操作具有安全隐患。一般来说,除非你有充分的理由,否则你应该避免将所有动词路由到动作。 我认为您可以使用含义相似的不同动词来实现约定以及用户的可读性和可理解性。

get 'deals/:id/retract', to: 'deals#retract'
post 'deals/:id/unsubscribe', to: 'deals#unsubscribe'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-31
    • 2021-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多