5

问题

我有一个父母,accepts_nested_attributes_for一个孩子。所以,当我有一个给父母的表格时,我需要给build孩子,这样我也可以为它显示表格字段。我想知道的是:我应该build把孩子放在哪里?在模型、视图或控制器中?

为什么我要问这个

你可能会摇头并认为我是一个疯子问这样的问题,但这是让我来到这里的思路。

我有一个模型Customer,像这样:accepts_nested_attributes_forbilling_address

class Customer
  belongs_to :billing_address, class_name: 'Address'
  accepts_nested_attributes_for :billing_address
end

当我Customer向用户展示新表单时,我想确保有一个空白billing_address,以便用户实际看到billing_address. 所以我的控制器中有这样的东西:

def new
  @customer = Customer.new
  @customer.build_billing_address
end

但是,如果用户没有填写任何billing_address字段,而是尝试提交无效的表单,他们将看到一个不再具有 的字段的表单,除非我在控制器billing_address的操作中放入类似的内容create

def create
  @customer = Customer.new(params[:customer])
  @customer.build_billing_address if @customer.billing_address.nil?
end

还有另一个问题,如果用户尝试编辑 a Customer,但Customer它还没有关联billing_address,他们将看不到billing_address. 所以我必须在控制器中添加这样的东西:

def edit
  @customer = Customer.find(params[:id])
  @customer.build_billing_address if @customer.billing_address.nil?
end

update在控制器的方法中也需要发生类似的事情。

不管怎样,这是高度重复的,所以我想在模型中做一些事情。我最初的想法是为模型的after_initialize事件添加一个回调,如下所示:

class CustomerModel
  after_initialize :build_billing_address, if: 'billing_address.nil?'
end

但我的蜘蛛侠感觉开始刺痛。谁能说我以后不会Customer在我的代码的其他部分实例化 a 并以一些意想不到的方式造成严重破坏。

所以我目前的想法是,最好的地方是表单视图本身,因为我想要完成的是为billing_address表单留一个空白,而表单本身是我知道的代码中唯一的地方确定我要为billing_address.

但是,你知道,我只是互联网上的某个人。我应该在哪里build_billing_address

4

5 回答 5

3

尽管Xavier Shay 的这条建议来自 2011 年,但他建议将其放在视图中,“因为这是视图问题(我们是否显示字段?)”

应用程序/helpers/form_helper.rb:

module FormHelper
  def setup_user(user)
    user.address ||= Address.new
    user
  end
end

应用程序/视图/用户/_form.html.erb:

<%= form_for setup_user(@user) do |f| %>

请注意,我必须将辅助方法更改为以下内容:

  def setup_user(user)
    user.addresses.build if user.addresses.empty?
    user
  end

控制器保持完全不变。

于 2015-03-15T05:57:22.997 回答
2

如果你知道你的模型应该总是有一个帐单地址,你可以在你的模型类中覆盖这个属性的getter,如文档中所述

def billing_address
    super || build_billing_address
end

build_billing_address根据您的特定需求,可以选择传入任何属性。

于 2013-12-27T14:55:06.507 回答
1

如果你想构建一些东西并在以后保存它,你会使用 build。我会说,在嵌套路由中使用它。

def create
 @address = @customer.billing_addresses.build(params[:billing_address])
 if @address.save
   redirect_to @customer.billing_addresses
 else
   render "create"
 end
end

类似的东西。当我在控制台中时,我也会使用构建。

于 2013-10-25T06:43:29.443 回答
1

您必须记住MVC的原则,即创建 DRY(不要重复自己)代码,该代码有效地分布在应用程序的各个移动部分之间

accepts_nested_attributes_for非常适合保持干燥

accepts_nested_attributes_for是一个模型函数,它允许您通过关联将数据传递到另一个模型。它存在的原因是让您能够基于单个表单填充另一个模型的数据,并且非常适合扩展功能而无需太多额外代码

你引用的问题是,如果你想在应用程序的其他区域使用代码,你最终会遇到各种各样的问题

我对此的反驳是为了创建尽可能高效的应用程序,您希望编写尽可能少的代码——让 Rails 处理所有事情。该accepts_nested_attributes_for功能确实允许您这样做,但显然是有代价的,因为每次您想使用它时都必须适应它

我的建议是使用你认为最有效的代码,但也要遵守约定;因为这将确保速度和效率

于 2013-10-25T08:16:51.340 回答
1

您应该在控制器中处理所有这些场景,因为它不是模型的责任。

就保持事物干燥而言,您可以编写一个方法,

def build_customer(customer)
  customer.build_billing_address if customer.billing_address.nil?
  #add more code if needed
end

在控制器内部,您可以在需要的任何地方调用此方法。例如

def create
  @customer = Customer.new(params[:customer])
  if @customer.save
    redirect_to @customer.billing_addresses
  else
    build_customer(@customer)
    render "new"
  end 
end
于 2015-03-16T13:50:58.543 回答