【问题标题】:Creating a nested model instance (has_many relation) on the view of its parent model在其父模型的视图上创建嵌套模型实例(has_many 关系)
【发布时间】:2017-04-15 08:38:55
【问题描述】:

我有一个模型 A 和一个模型 B,关系是 A has_many B(和 B 属于 A)。我有一个模型,一个控制器和 A 的视图,只有一个 B 的模型。 我想在 A ( url/a/1/edit) 的编辑视图上创建和编辑 B 的实例。

我知道我可以为 B 创建一个控制器并在 A 的视图中使用表单调用这些方法,但是我需要重定向回 A 的视图,因为我不想要 B 的实际视图。

有推荐的方法吗?我想要的是不破坏 Rails 提供的任何帮助程序(例如,在转发之后,我认为收到错误消息和类似的东西很痛苦)。

提前致谢!

【问题讨论】:

  • 尝试gem "cocoon" 用于嵌套表单您可以在此处获取文档github.com/nathanvda/cocoon
  • 如果您不需要视图输入,您可以简单地在模型 A 上使用回调方法(before_save/after_save)。因此,当您的编辑方法保存更新的模型 A 时,将触发回调并然后,您可以在该回调中编辑关联的模型。如果您需要从视图中编辑嵌套模型的字段,那么最好使用@Mayank 引用的 gem "cocoon"。

标签: ruby-on-rails rails-activerecord ruby-on-rails-5 has-many


【解决方案1】:

在模型级别上,您将使用accepts_nested_attributes_for

class A < ApplicationModel
  has_many :bs
  accepts_nested_attributes_for :bs
  validates_associated :bs
end

class B < ApplicationModel
  belongs_to :a
end

这允许 A 获取属性并通过将属性 bs_attributes 与属性数组一起传递来创建嵌套的 bsvalidates_associated可以用来保证A不能持久化,bs也无效。

要创建nested form fields,请使用fields_for

<%= form_for(@a) do |f| %>
  # field on A
  <%= f.text_input :foo %>
  # creates a fields for each B associated with A.
  <%= f.fields_for(:bs) do |b| %>
    <%= b.text_input :bar %>
  <% end %>
<% end %>

whitelist nested attributes 使用带有子记录允许属性数组的哈希键:

params.require(:a)
      .permit(:foo, bs_attributes: [:id, :bar])

在创建新记录时,如果您希望存在用于创建嵌套记录的输入,您还必须“播种”表单:

class AsController < ApplicationController

  def new
    @a = A.new
    seed_form
  end

  def create
    @a = A.new(a_params)
    if @a.save 
      redirect_to @a
    else
      seed_form
      render :new
    end
  end

  def update
    if @a.update(a_params) 
      redirect_to @a
    else
      render :edit
    end
  end

  private

  def seed_form
    5.times { @a.bs.new } if @a.bs.none?
  end

  def a_params
    params.require(:a)
          .permit(:foo, bs_attributes: [:id, :bar])
  end
end

编辑: seed_form 也可以只添加一个并且每次都这样做。所以你总是有一个“空”的要添加。如果没有填充,您需要确保在保存之前过滤掉空的,将accepts_nested_attributes_for更改为:

accepts_nested_attributes_for :bs, reject_if: proc { |attr| attr['bar'].blank? }

【讨论】:

  • 这通常是 Rails 中最难理解的部分之一,我要求您在提出后续问题之前仔细阅读此答案中的文档链接。
  • 不——你不需要茧宝石。它确实使处理嵌套记录的某些方面更容易,但也抽象出许多重要知识。
  • 非常感谢您的详细解答。如果我有后续问题,我会调查并回来。 (并在我完成后将答案标记为正确。g
  • 我现在只有一个后续问题。使用这种方法,一切都在 A 的控制器中处理。在设计或要求的情况下,为 B 创建嵌套控制器需要什么不同?只是为了专门的意见吗?还是有任何其他理由更喜欢一种方法而不是另一种?
  • 好的,我已经尝试以完全相同的方式实现这一点。当我创建 A 的实例(带有 B 的实例)时,它工作正常,但是当我开始编辑它时,它也会复制 B 的所有实例。虽然 B 的现有实例的 ID 存在隐藏字段。我错过了什么吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-28
  • 2017-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多