在同一页面上使用相同的表单进行新建和编辑操作 (simple_form & Rails)

Use the same form on the same page for new and edit actions (simple_form & Rails)

我正在学习 Rails 并希望能够在同一页面上使用相同的表单来添加元素(此处为 "dose")或通过预填充表单(页面刷新后,我不需要动态地做)。

这是页面的屏幕截图 (views/cocktails/show.html.erb)

创建新的 "dose" 工作正常,但在尝试编辑 Rails 时会引发 "DosesController#edit is missing a template for this request format and variant" 错误。

因为我试图真正理解路由逻辑,所以我这样做只是为了学习,即使这可能是一种不好的做法,我想通过 Rails 仅。

views/doses/_form.html.erb:

我将逻辑放入部分表单中,以决定是创建还是更新@dose:

<% if @dose.new_record? %>
  <%= simple_form_for [@cocktail, @dose] do |f| %>
    <%= f.input :description %>
    <%= f.association :ingredient %>
    <%= f.button :submit %>
  <% end %>
<% else %>
  <%= simple_form_for @dose, url: url_for("doses#update") do |f| %>
    <%= f.input :description %>
    <%= f.association :ingredient %>
    <%= f.button :submit %>
  <% end %>
<% end %>

views/cocktails/show.html.erb:

我正在调用一个 "new or edit" 路径助手,将鸡尾酒 ID 和剂量 ID 传递给它,因为剂量属于一种鸡尾酒。

  <% @cocktail.doses.each do |dose| %>
    <li>
    <h4><%= dose.description %> - <%= dose.ingredient.name %></h4>
    <%= link_to "<i class=\"fas fa-pencil-alt\"></i>".html_safe, new_or_edit_dose_path(@cocktail.id, dose.id) %>
    <%= link_to "<i class=\"far fa-trash-alt\"></i>".html_safe, dose_path(dose.id), method: :delete, data: {confirm: "Are you sure?"} %>
    </li>
  <% end %>
  </ul>
  <%= render "doses/form" %>
  <%= link_to "Back", cocktails_path %>
</div>

routes.rb

此路径助手绑定到 GET 路由,与剂量控制器的编辑操作相关联。因此,我根本不会使用 doses#new 路由操作。

  root to: "cocktails#index"
  resources :cocktails, only: [:index, :show, :new, :create] do
    resources :doses, only: [ :create]
  end
  resources :doses, only: [ :update, :destroy]
  get "cocktails/:id/doses/:dose_id", to: "doses#edit", as: :new_or_edit_dose

doses_controller.rb

  def edit
    @cocktail = Cocktail.find(params[:id])
    @dose = Dose.find(params[:dose_id])
  end

  def update
    if @dose.update(set_params)
      redirect_to cocktail_path(@dose.cocktail)
    else
      render(:edit)
    end
  end

  def set_params
    params.require(:dose).permit(:description, :ingredient_id)
  end

我对问题的理解

我想问题是我使用的是 cocktails/:id/doses/:id 路由,但在 cocktails/:id/show 视图上显示表单。我尝试在 doses#edit 的末尾使用 redirect_to 并将参数传递给它,但它没有用。

有人可以告诉我这是否可行,并提示我应该寻找什么吗?

解决方案:

关于

... but when trying to edit Rails raises the DosesController#edit is missing a template for this request format and variant

... 表示您缺少文件 app/views/doses/edit.html.erb。所以只需创建该文件,或者...因为您说...

I guess the problem is that I'm using a cocktails/:id/doses/:id route, but displaying the form on a cocktails/:id/show view

... 如果理解正确,您希望此 url cocktails/:id/doses/:id 显示与您的 cocktails/:id/ 完全相同的页面(可能映射到您的 cocktails#show页面,那么您只需执行以下操作:

app/controllers/doses_controller.rb:

# ...
def edit
  @cocktail = Cocktail.find(params[:id])
  @dose = Dose.find(params[:dose_id])

  render 'cocktails/show'

  # rails by default has an invisible line at the end of the actions, unless `render` is called manually like line above. But removing the line above, you'll get something like below
  # render 'CONTROLLER_NAME/ACTION_NAME.FORMAT.TEMPLATE_ENGINE'
  # so in this specific case:
  #   CONTROLLER_NAME == doses
  #   ACTION_NAME == edit
  #   FORMAT == html
  #   TEMPLATE_ENGINE == erb
  # which translates to:
  #   render 'doses/edit.html.erb'
  # which you can also do like the following
  #   render 'edit.html.erb' # because same controller name as the template being called
  # which you can also do like the following
  #   render 'edit' # because it automatically identifies what format using the value of `request.format`, and what default template engine your Rails app is configured to use
  # ... and this is the reason why you got that error "DosesController#edit is missing a template...", because app/views/doses/edit.html.erb does not exist, which by default is being rendered

推荐:

  • 请先说明为什么要走下面的路线,我看看有没有更好的办法,我觉得:

    get "cocktails/:id/doses/:dose_id", to: "doses#edit", as: :new_or_edit_dose
    

... 我问这个是因为通常 "create or update" / "new or edit" 请求是针对某个唯一属性标识符进行路由的。例如,在您的情况下,唯一属性标识符似乎是 :dose_id,因为如果 :dose_id 尚不存在,您 "create" 一个新的 Dose 记录,或者您 "update" Dose 记录(如果它已经存在且 id 值等于 :dose_id)。现在,这是我的意见,但我认为从手动设置的 "id" 创建 Dose 记录不是一个好主意,因为您通常让数据库自动分配主键 id 值。

...为了解释我的观点,例如,为了模拟上面所说的路线,假设我在浏览器中打开这个URL:"/cocktails/1/doses/1",到目前为止这么好假设Dose(id: 1) 已经存在,那么我只会在页面上看到一个表单来更新 Dose。但是,假设我打开 "/cocktails/1/doses/9999999999",假设我打开 Dose(id: 9999999999) does not exist yet,然后我将看到一个用于创建 Dose... 的表单,这在此时仍然有效。当您已经删除了 Dose 记录时,就会出现问题。假设 Dose(id: 1) 已经被删除,然后我再次尝试打开 "/cocktails/1/doses/1",因为它已经被删除,然后我会看到一个创建 Dose 的表单(仍然是好的),但会出现问题,因为您正在重用 id 值,该值应该是 "primary key" 并且应该唯一标识资源,因为依赖于此唯一 id 值的关联/模型可能会被搞砸: 例如,我有一个 Car 属性 {name: 'Porsche', category_id: 23} 的记录, belongs_to 一个 Category(23) 具有属性 {name: 'vehicle'} 的记录,然后不知何故出乎意料Category(23)已被销毁,然后用相同的ID重新创建!但是这次创建的属性可以说已经变成了 {name: 'fruit'}。现在,我的保时捷车变成了水果!!

但是,我可以想到您故意这样做的一些原因(即,如果您想将 id 值视为 uuid,这可能永远不会以编程方式重用),但是我上面的想法只是为了以防万一你还没有意识到这些可能的后果。