带有多个模型的Rails结帐表单

时间:2010-01-27 17:44:36

标签: ruby-on-rails forms models nested-forms

我正在基于Agile Web Development With Rails(版本3)中的购物车创建购物车。我设置了“商品”添加到“购物车”的位置,然后在开始结帐流程时,它们被添加到“订单”对象作为“line_items”。 “line_items”代表任何数量的一个“项目”。到目前为止,我并没有偏离书中的例子。但是,这对我来说很复杂。我商店中的每个“商品”都可以使用文字进行自定义,我需要能够在“订单”中使用“line_items”存储自定义文本。

如上所述,“line_items”包含任何数量的“item”,但客户需要能够自定义每个项目,因此每个“line_item”必须为每个“item”保留不同的自定义项。因此,“line_items”表中不能只有一列用于自定义。我决定组织它的方式是创建一个新的模型/表“line_item_attributes”。对于“line_item”中的每个“item”,都有一个新的“line_item_attributes”。

我还是Rails的新手,我在使用它时遇到了一些麻烦。我不相信我甚至会做“正确的方式”。我遇到的是一种鸡/蛋问题。当我创建“订单”时,我将购物车中的“商品”添加为“line_items”。现在,为了定制他们订购的产品,我还必须为每个“line_item”添加“line_item_attributes”,以便自定义表单可以使用。

以下是我不知道的内容:我不知道在客户提交表单后如何“填写”空白“line_item_attributes”。我无法为表单创建“虚拟”line_item_attributes,然后在提交的数据中提交创建新的(实际将保存的)。原因是它们必须与它们所属的“line_items”联系在一起。当我打电话给“@ order.save”时,我曾希望Rails会填充它们,但事实并非如此。我希望这不难理解。

我在下面列出了相关代码:

buy.rb(控制器)

-SNIP-
def purchase
  @cart  = find_cart

  if @cart.items.empty?
   redirect_to_index("Your order is empty")
  end
end

def customize
  @renderable_partials = [1, 2, 3]
  @order = find_order

  if @order.nil?
   redirect_to_index("Your order is empty")
   end
end

def save_purchase
 @cart  = find_cart
 @order = find_order(params[:cart_owner])
 @order.add_line_items_from_cart(@cart)

 redirect_to :action => 'customize'
end

def save_customize
 @order = find_order

 if @order.save
  redirect_to :action => 'purchase'
 else
  flash[:error] = "Your information could not be saved"
  redirect_to :action => 'customize'
 end
end
-SNIP-

order.rb(型号)

class Order < ActiveRecord::Base
 has_many :line_items
 has_many :line_item_attributes
 accepts_nested_attributes_for :line_items
 accepts_nested_attributes_for :line_item_attributes

 def add_line_items_from_cart(cart)
  cart.items.each do |item|
   li = LineItem.from_cart_item(item)
   line_items << li
  end
 end
end

line_item.rb(型号)

class LineItem < ActiveRecord::Base
 belongs_to :order
 belongs_to :item
 has_many :line_item_attributes
 accepts_nested_attributes_for :line_item_attributes

 def self.from_cart_item(cart_item)
  li = self.new
  li.item = cart_item.item
  li.quantity = cart_item.quantity
  li.total_price = cart_item.price

  li.quantity.times do |single_item|
   lia = LineItemAttribute.new
   li.line_item_attributes << lia
  end

  li
 end
end

line_item_attributes.rb(型号)

class LineItemAttribute < ActiveRecord::Base
 belongs_to :order
 belongs_to :line_item
end

感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

我建议将Order和LineItems创建移动到单独的&#34;服务对象&#34;或&#34;形成对象。&#34;在服务/表单对象内,将订单和行项目创建包装在单个事务中。代码将更易于阅读,您的模型不会受到跨模型的污染。从结账控制器,将@cart对象传递给服务对象,而不是直接调用Order对象。

有关服务对象的更多信息,请查看此帖子的#2和#3:http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/