如何在控制器中渲染 :new 并且不丢失 url 中的参数?

How to do render :new in controller and do not lose params from url?

我想在控制器的创建方法中执行 render :new,问题是,它更改了 url,并且在 url 中我需要一个参数在我的 html 中填写表格(例如:localhost3000/episodes/new?serie_id=8,我用 hidden_field_tag 来完成)。

我的控制器:

  def new
    @episode= Episode.new
  end

  def create
    # params = { episode: { name: valor, serie_id: valor, ... more} }
    @episode = Episode.new(episode_permits)
    if @episode.save
      redirect_to @episode
    else
      render :new #This render I want to fix
    end
  end

我的部分 _form.html.erb:

  <%= form_for @chapter do |f| %>
        <%= f.label :name, 'Name' %>
        <%= f.text_field :name%> <!-- I don't want to use required: true -->
        <%= f.hidden_field :serie_id, value: params[:serie_id] %>
  <% end %>

如果用户按下从新建到创建表单的按钮,如果他在填写表单时出错,@episode 将无法保存并生成 render :new 然后 url 改为 localhost3000/episodes/

render :new不改url怎么办?

我尝试使用 redirect_to new_episode_path(:serie_id => params[:episode][:serie_id]) 而不是 render :new,但是这样会丢失用户在表单中输入的内容,并且任何验证都不会显示发生了什么,所以这不是一个选项。

关于项目的更多信息,如果它是模棱两可的:我正在制作一个 Netflix 网络来学习 Rails,一个系列(名称)有很多剧集和一个剧集(名称,serie_id)属于一个系列。

我会改变

<%= f.hidden_field :serie_id, value: params[:serie_id] %>

<%= f.hidden_field :serie_id, value: (f.object.serie_id.presence || params[:serie_id]) %>

仅在 serie_id 属性尚未在章节上设置时回退到 params[:serie_id]

rails 方法是创建一个 nested resource:

# config/routes.rb
resources :series do
  resources :episodes, shallow: true
end

运行 rails routes 查看路由输出。这里的关键是它创建了一个 GET /series/:series_id/episodes/new 路由和一个 POST /series/:series_id/episodes。这使得两个资源之间的链接变得明确 - 而不是隐藏在请求正文中的东西。

一般来说,如果您使用隐藏字段来传递资源 ID 或用户 ID,那您就错了。

class EpisodesController
  before_action :set_series

  # GET /series/1/episodes/new
  def new
    @episode = @series.episodes.new
  end

  # POST /series/1/episodes
  def create
    @episode = @series.episodes.new(episode_params)
    if @episode.save
       redirect_to @episode
    else
       render :new
    end
  end
  # ...
  private 
  def set_series
     @series = Series.find(params[:series_id])
  end

  def episode_params
    params.require(:episode).permit(:foo, :bar, :baz)
  end
end

为了将 post 的形式变为 /series/:serie_id/episodes 而不是 /series 我们传递一个数组:

<%= form_for([@series, @episode]) %>
  <%= f.label :name, 'Name' %>
  <%= f.text_field :name%>
  <%= f.submit %>
<% end %>

If the user press the button which sumbit the form from new to create, if he makes a mistake filling the form, the @episode can't been save and makes the render :new and then the url change to localhost3000/episodes/

这是一个非常常见的初学者挂断,这是由于不了解 Rails style REST 的工作原理造成的

在 REST 中,您可以通过 posting 到收集路由 (POST /episodes) 来创建资源。这意味着浏览器 URL 通过 post 表单更改,这正是它应该的样子。

render :new 不会改变 URL。它只是指示 rails 呈现 app/views/episodes/new.html.erb 视图。

您随后看到的是执行非幂等操作的结果。你不应该重定向回去,因为它既不必要又要求你将所有参数放在不太理想的查询字符串中。

PS

您还应注意 correct singular form of series in English 系列 而不是 系列 。不确定 rails 是否会正确地变形,但如果不是,您可以通过设置变形来做到这一点。

# config/intializers/inflections.rb
ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.irregular 'series', 'series'
en

然后您应该重命名模型和关联。

为什么这很重要? The principle of least suprise.